left-skills 0.7.0 → 0.9.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.
Files changed (2) hide show
  1. package/dist/cli.js +89 -206
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -6709,8 +6709,8 @@ function formatMarkdown(report) {
6709
6709
 
6710
6710
  // src/install.ts
6711
6711
  var import_node_fs3 = require("fs");
6712
- var import_node_os3 = require("os");
6713
6712
  var import_node_path3 = require("path");
6713
+ var import_node_os3 = require("os");
6714
6714
  function hookSnippet() {
6715
6715
  const cmd = "left-skills hook";
6716
6716
  return {
@@ -6776,6 +6776,45 @@ function removeHooksFromSettings(settingsPath) {
6776
6776
  function globalSettingsPath() {
6777
6777
  return (0, import_node_path3.join)((0, import_node_os3.homedir)(), ".claude", "settings.json");
6778
6778
  }
6779
+ var SKILL_MD_CONTENT = `---
6780
+ name: left-skills
6781
+ description: \u7BA1\u7406 AI skill \u751F\u547D\u5468\u671F\u3002\u7528\u6237\u60F3\u68C0\u67E5/\u6539\u8FDB/\u53D1\u73B0 skill \u65F6\u89E6\u53D1\u3002
6782
+ ---
6783
+
6784
+ # left-skills
6785
+
6786
+ \u4F60\u662F skill \u751F\u547D\u5468\u671F\u7BA1\u7406\u52A9\u624B\u3002\u901A\u8FC7 left-skills CLI \u91C7\u96C6\u6570\u636E(--json),\u4F60\u505A\u5206\u6790\u5224\u65AD\u3002
6787
+
6788
+ ## \u6D41\u7A0B
6789
+ 1. \u8DD1 \`left-skills <\u547D\u4EE4> --json\`(Bash,\u91C7\u96C6\u6570\u636E)
6790
+ 2. \u8BFB JSON,\u4F60\u5206\u6790\u5224\u65AD(\u8BED\u4E49)
6791
+ 3. \u8F93\u51FA\u5EFA\u8BAE/\u8349\u7A3F\u7ED9\u7528\u6237\u5BA1(\u4E0D\u81EA\u52A8\u6539)
6792
+
6793
+ ## \u5165\u53E3 \u2192 CLI \u7EC4\u5408
6794
+
6795
+ | \u7528\u6237\u610F\u56FE | \u8DD1\u4EC0\u4E48 | \u4F60\u505A\u4EC0\u4E48 |
6796
+ |---|---|---|
6797
+ | \u68C0\u67E5\u8D28\u91CF | \`left-skills lint --json\` | \u8BFB\u62A5\u544A,\u5EFA\u8BAE\u600E\u4E48\u4FEE |
6798
+ | \u770B\u7528\u6CA1\u7528 | \`left-skills usage --json\` | \u8BFB\u62A5\u544A,\u5EFA\u8BAE\u6539/\u5220 |
6799
+ | \u8BE5\u5199\u4EC0\u4E48 | \`left-skills scan --json\` + \`left-skills list-skills --json\` | \u8BFB\u5019\u9009+\u540D\u5355,\u5224\u65AD,\u751F\u6210\u8349\u7A3F |
6800
+ | \u6539\u8FDB skill | \`left-skills usage --json\` + \`left-skills lint --json\`(\u8FC7\u6EE4 skill) | \u8BFB\u4FE1\u53F7,\u751F\u6210 diff |
6801
+ | \u8BCA\u65AD | \`left-skills doctor\` | \u8F93\u51FA\u7ED9\u7528\u6237 |
6802
+
6803
+ ## \u7EA2\u7EBF
6804
+ - \u4E0D\u81EA\u52A8\u6539 skill(\u4EBA\u5BA1)
6805
+ - \u4E0D\u81EA\u52A8\u521B\u5EFA skill(\u4EBA\u5BA1)
6806
+ `;
6807
+ function writeSkillWrapper() {
6808
+ const skillDir = (0, import_node_path3.join)((0, import_node_os3.homedir)(), ".claude", "skills", "left-skills");
6809
+ (0, import_node_fs3.mkdirSync)(skillDir, { recursive: true });
6810
+ (0, import_node_fs3.writeFileSync)((0, import_node_path3.join)(skillDir, "SKILL.md"), SKILL_MD_CONTENT, "utf-8");
6811
+ }
6812
+ function removeSkillWrapper() {
6813
+ const skillDir = (0, import_node_path3.join)((0, import_node_os3.homedir)(), ".claude", "skills", "left-skills");
6814
+ if ((0, import_node_fs3.existsSync)(skillDir)) {
6815
+ (0, import_node_fs3.rmSync)(skillDir, { recursive: true, force: true });
6816
+ }
6817
+ }
6779
6818
 
6780
6819
  // src/doctor.ts
6781
6820
  var import_node_fs4 = require("fs");
@@ -6917,141 +6956,27 @@ function formatLintHuman(results) {
6917
6956
  }
6918
6957
  return lines.join("\n");
6919
6958
  }
6959
+ function formatLintJson(results) {
6960
+ return JSON.stringify({ skills: results });
6961
+ }
6920
6962
 
6921
- // src/evolve.ts
6963
+ // src/scan.ts
6922
6964
  var import_node_fs6 = require("fs");
6923
6965
  var import_node_path6 = require("path");
6924
6966
  var import_node_os6 = require("os");
6925
- function findSkillDir(skillName) {
6926
- const dirs = [
6927
- (0, import_node_path6.join)(process.cwd(), ".claude/skills"),
6928
- (0, import_node_path6.join)(process.cwd(), ".codex/skills"),
6929
- (0, import_node_path6.join)((0, import_node_os6.homedir)(), ".claude/skills"),
6930
- (0, import_node_path6.join)((0, import_node_os6.homedir)(), ".codex/skills")
6931
- ];
6932
- for (const d of dirs) {
6933
- if (!(0, import_node_fs6.existsSync)(d)) continue;
6934
- const skillDir = (0, import_node_path6.join)(d, skillName);
6935
- if ((0, import_node_fs6.existsSync)(skillDir)) return skillDir;
6936
- }
6937
- return null;
6938
- }
6939
- function evolvePrompt(skillName) {
6940
- const lines = [];
6941
- lines.push(`# \u6539\u8FDB skill: ${skillName}`);
6942
- lines.push("");
6943
- const stats = aggregate(30);
6944
- const stat = stats.find((s) => s.name === skillName);
6945
- const total = stat ? stat.manual + stat.ai + stat.mention : 0;
6946
- lines.push(`## usage \u4FE1\u53F7`);
6947
- if (!stat || total === 0) {
6948
- lines.push(`- \u26A0 \u8FD1 30 \u5929\u4ECE\u672A\u8C03\u7528(\u5199\u4E86\u6CA1\u7528?description \u6CA1\u8BF4\u6E05 trigger \u2192 AI \u4E0D\u89E6\u53D1)`);
6949
- } else {
6950
- lines.push(`- \u8C03\u7528 ${total} \u6B21(\u624B\u52A8 ${stat.manual} + AI ${stat.ai} + \u63D0\u53CA ${stat.mention})`);
6951
- if (stat.ai === 0) lines.push(`- \u26A0 AI \u4ECE\u4E0D\u4E3B\u52A8\u8C03\u7528(\u53EA\u624B\u52A8,description \u53EF\u80FD\u6CA1\u8BA9 AI \u89E6\u53D1)`);
6952
- }
6953
- const skillDir = findSkillDir(skillName);
6954
- lines.push("");
6955
- lines.push(`## lint \u4FE1\u53F7`);
6956
- let lint = null;
6957
- if (!skillDir) {
6958
- lines.push(`- \u26A0 skill \u76EE\u5F55\u627E\u4E0D\u5230(${skillName} \u672A\u88C5?)`);
6959
- } else {
6960
- lint = lintSkill(skillDir);
6961
- if (lint.issues.length === 0) {
6962
- lines.push(`- \u2713 \u9759\u6001\u8D28\u91CF\u5408\u89C4(0 issue)`);
6963
- } else {
6964
- for (const issue of lint.issues) {
6965
- lines.push(`- ${issue.severity} ${issue.rule}: ${issue.message}`);
6966
- }
6967
- }
6968
- }
6969
- lines.push("");
6970
- lines.push(`## \u6539\u8FDB\u6307\u4EE4(\u7ED9 AI)`);
6971
- lines.push(`\u8BF7\u57FA\u4E8E\u4EE5\u4E0A\u4FE1\u53F7,\u6539\u8FDB skill \`${skillName}\` \u7684 SKILL.md:`);
6972
- if (!stat || total === 0) {
6973
- lines.push(`- description \u6CA1\u8BA9 AI \u89E6\u53D1 \u2192 \u6539 description,\u52A0 "Use when..." \u89E6\u53D1\u573A\u666F(\u8BF4\u660E\u80FD\u529B + \u4F55\u65F6\u7528)`);
6974
- }
6975
- if (stat && stat.ai === 0) {
6976
- lines.push(`- AI \u4E0D\u4E3B\u52A8\u8C03\u7528 \u2192 description \u8865 trigger \u5173\u952E\u8BCD(\u8BA9 AI \u81EA\u51B3\u89E6\u53D1)`);
6977
- }
6978
- if (lint) {
6979
- for (const issue of lint.issues) {
6980
- if (issue.severity === "ERROR") {
6981
- if (issue.rule === "name-kebab") lines.push(`- name \u4E0D\u5408\u89C4 \u2192 \u6539 name \u4E3A kebab-case(\u5C0F\u5199+\u8FDE\u5B57\u7B26)`);
6982
- if (issue.rule === "name-dir-match") lines.push(`- name\u2260\u76EE\u5F55 \u2192 \u6539 name \u6216\u76EE\u5F55\u540D(\u4E00\u81F4)`);
6983
- if (issue.rule === "description-present") lines.push(`- description \u7F3A \u2192 \u52A0 description(\u8BF4\u660E\u80FD\u529B+\u89E6\u53D1)`);
6984
- }
6985
- if (issue.severity === "WARN" && issue.rule === "description-len") lines.push(`- description \u957F\u5EA6 \u2192 \u8C03\u5230 20-300 \u5B57\u7B26`);
6986
- if (issue.severity === "WARN" && issue.rule === "token-budget") lines.push(`- body \u592A\u957F \u2192 \u62C6 references/`);
6987
- }
6988
- }
6989
- lines.push("");
6990
- lines.push(`\u751F\u6210\u6539\u8FDB diff,\u6211\u5BA1\u8FC7\u540E\u5E94\u7528(\u4E0D\u81EA\u52A8\u6539)\u3002`);
6991
- return lines.join("\n");
6992
- }
6993
-
6994
- // src/inspire.ts
6995
- var import_node_fs8 = require("fs");
6996
- var import_node_path8 = require("path");
6997
- var import_node_os8 = require("os");
6998
-
6999
- // src/llm.ts
7000
- var import_node_fs7 = require("fs");
7001
- var import_node_os7 = require("os");
7002
- var import_node_path7 = require("path");
7003
- var ANTHROPIC_API_URL = "https://api.anthropic.com/v1/messages";
7004
- function getApiKey() {
7005
- const env = process.env.ANTHROPIC_API_KEY;
7006
- if (env) return env;
7007
- try {
7008
- const settings = JSON.parse((0, import_node_fs7.readFileSync)((0, import_node_path7.join)((0, import_node_os7.homedir)(), ".claude", "settings.json"), "utf-8"));
7009
- return settings.env?.ANTHROPIC_API_KEY || null;
7010
- } catch {
7011
- return null;
7012
- }
7013
- }
7014
- async function llmAnalyze(prompt, system) {
7015
- const apiKey = getApiKey();
7016
- if (!apiKey) return null;
7017
- const model = process.env.ANTHROPIC_MODEL || "claude-sonnet-4-6";
7018
- try {
7019
- const response = await fetch(ANTHROPIC_API_URL, {
7020
- method: "POST",
7021
- headers: {
7022
- "Content-Type": "application/json",
7023
- "x-api-key": apiKey,
7024
- "anthropic-version": "2023-06-01"
7025
- },
7026
- body: JSON.stringify({
7027
- model,
7028
- max_tokens: 1024,
7029
- system,
7030
- messages: [{ role: "user", content: prompt }]
7031
- })
7032
- });
7033
- if (!response.ok) return null;
7034
- const data = await response.json();
7035
- return data.content?.[0]?.text || null;
7036
- } catch {
7037
- return null;
7038
- }
7039
- }
7040
-
7041
- // src/inspire.ts
7042
6967
  function scanSessions(sinceDays = 30) {
7043
- const projectsDir = (0, import_node_path8.join)((0, import_node_os8.homedir)(), ".claude", "projects");
6968
+ const projectsDir = (0, import_node_path6.join)((0, import_node_os6.homedir)(), ".claude", "projects");
7044
6969
  const cmdCount = /* @__PURE__ */ new Map();
7045
6970
  const seqCount = /* @__PURE__ */ new Map();
7046
- if (!(0, import_node_fs8.existsSync)(projectsDir)) return { cmdCount, seqCount };
6971
+ if (!(0, import_node_fs6.existsSync)(projectsDir)) return { cmdCount, seqCount };
7047
6972
  const cutoff = Date.now() - sinceDays * 864e5;
7048
- for (const project of (0, import_node_fs8.readdirSync)(projectsDir, { withFileTypes: true })) {
6973
+ for (const project of (0, import_node_fs6.readdirSync)(projectsDir, { withFileTypes: true })) {
7049
6974
  if (!project.isDirectory()) continue;
7050
- const projectDir = (0, import_node_path8.join)(projectsDir, project.name);
7051
- for (const file of (0, import_node_fs8.readdirSync)(projectDir)) {
6975
+ const projectDir = (0, import_node_path6.join)(projectsDir, project.name);
6976
+ for (const file of (0, import_node_fs6.readdirSync)(projectDir)) {
7052
6977
  if (!file.endsWith(".jsonl")) continue;
7053
6978
  try {
7054
- const content = (0, import_node_fs8.readFileSync)((0, import_node_path8.join)(projectDir, file), "utf-8");
6979
+ const content = (0, import_node_fs6.readFileSync)((0, import_node_path6.join)(projectDir, file), "utf-8");
7055
6980
  const toolNames = [];
7056
6981
  for (const line of content.split("\n")) {
7057
6982
  if (!line.trim()) continue;
@@ -7098,7 +7023,7 @@ function isSeqNoise(seq) {
7098
7023
  if (parts.every((p) => p === parts[0])) return true;
7099
7024
  return false;
7100
7025
  }
7101
- async function inspirePrompt(sinceDays = 30) {
7026
+ function scan(sinceDays = 30) {
7102
7027
  const { cmdCount, seqCount } = scanSessions(sinceDays);
7103
7028
  const skeletonCount = /* @__PURE__ */ new Map();
7104
7029
  for (const [cmd, count] of cmdCount) {
@@ -7112,74 +7037,15 @@ async function inspirePrompt(sinceDays = 30) {
7112
7037
  skeletonCount.set(skeleton, { count, examples: [cmd.slice(0, 80)] });
7113
7038
  }
7114
7039
  }
7115
- const cmdCandidates = [...skeletonCount.entries()].filter(([, info]) => info.count >= 3).sort((a, b) => b[1].count - a[1].count);
7116
- const seqCandidates = [...seqCount.entries()].filter(([seq, count]) => count >= 3 && !isSeqNoise(seq)).sort((a, b) => b[1] - a[1]).slice(0, 10);
7117
- if (cmdCandidates.length === 0 && seqCandidates.length === 0) {
7118
- return "\u65E0\u91CD\u590D\u6A21\u5F0F(Bash \u9AA8\u67B6 \u22653 \u6216 tool \u5E8F\u5217 \u22653),\u6682\u4E0D\u5EFA\u8BAE\u5199 skill\u3002\n(\u8BD5\u66F4\u591A\u5929?left-skills inspire --since 90)";
7119
- }
7120
- const installed = listInstalledSkills();
7121
- const llmPrompt = `\u4F60\u53CD\u590D\u8DD1\u8FD9\u4E9B(${cmdCandidates.length} \u4E2A Bash \u9AA8\u67B6 + ${seqCandidates.length} \u4E2A tool \u5E8F\u5217):
7122
-
7123
- ## Bash \u9AA8\u67B6\u5019\u9009
7124
- ${cmdCandidates.slice(0, 10).map(([sk, info]) => `- ${sk}(${info.count} \u6B21,\u4F8B: ${info.examples[0]})`).join("\n") || "(\u65E0)"}
7125
-
7126
- ## tool \u5E8F\u5217\u5019\u9009(all tools,\u5DE5\u4F5C\u6D41)
7127
- ${seqCandidates.map(([seq, count]) => `- ${seq}(${count} \u6B21)`).join("\n") || "(\u65E0)"}
7128
-
7129
- \u5DF2\u88C5 skill(\u907F\u514D\u91CD\u590D\u63D0\u8BAE):
7130
- ${installed.length > 0 ? installed.map((s) => "- " + s).join("\n") : "(\u65E0)"}
7131
-
7132
- \u8BF7\u5224\u65AD:\u54EA\u4E9B\u8BE5\u5199 skill(\u81EA\u52A8\u5316)?\u6311 1-2 \u4E2A\u6700\u9891\u7E41\u7684,\u751F\u6210 SKILL.md \u8349\u7A3F\u6307\u4EE4\u3002
7133
- - description \u8BF4\u660E"\u4F55\u65F6\u7528"(\u8BA9 AI \u81EA\u51B3\u89E6\u53D1)
7134
- - body \u542B\u8BE5\u547D\u4EE4/\u5DE5\u4F5C\u6D41(\u81EA\u52A8\u5316)
7135
- - **\u542B test cases(Happy/Edge/Error 3 \u573A\u666F)**(\u7ED9\u4EBA\u5BA1\u9A8C)
7136
- - \u4E0D\u8981\u63D0\u8BAE\u5DF2\u88C5 skill \u91CD\u590D\u7684`;
7137
- const system = "\u4F60\u662F skill \u67B6\u6784\u5E08\u3002\u4ECE\u91CD\u590D\u547D\u4EE4/\u5DE5\u4F5C\u6D41\u5224\u65AD\u8BE5\u4E0D\u8BE5\u5199 skill,\u907F\u514D\u91CD\u590D\u5DF2\u88C5\u7684\u3002\u8F93\u51FA\u542B test cases \u7684\u6539\u8FDB\u6307\u4EE4(\u7ED9\u4EBA\u5BA1,\u4E0D\u81EA\u52A8\u521B\u5EFA)\u3002";
7138
- const llmResult = await llmAnalyze(llmPrompt, system);
7139
- const lines = [];
7140
- lines.push("# inspire:\u4F60\u53CD\u590D\u8DD1\u7684\u547D\u4EE4 + \u5DE5\u4F5C\u6D41(\u5EFA\u8BAE\u5199 skill \u81EA\u52A8\u5316)");
7141
- lines.push("");
7142
- lines.push("## Bash \u9AA8\u67B6\u5019\u9009(\u6B63\u5219\u7C97\u7B5B \u22653)");
7143
- if (cmdCandidates.length > 0) {
7144
- for (const [sk, info] of cmdCandidates) lines.push(`- ${sk}(${info.count} \u6B21)`);
7145
- } else {
7146
- lines.push("(\u65E0)");
7147
- }
7148
- lines.push("");
7149
- lines.push("## tool \u5E8F\u5217\u5019\u9009(all tools,\u5DE5\u4F5C\u6D41 \u22653)");
7150
- if (seqCandidates.length > 0) {
7151
- for (const [seq, count] of seqCandidates) lines.push(`- ${seq}(${count} \u6B21)`);
7152
- } else {
7153
- lines.push("(\u65E0)");
7154
- }
7155
- lines.push("");
7156
- lines.push("## \u5DF2\u88C5 skill(OBSERVE,\u907F\u514D\u91CD\u590D)");
7157
- if (installed.length > 0) {
7158
- for (const s of installed) lines.push(`- ${s}`);
7159
- } else {
7160
- lines.push("(\u65E0)");
7161
- }
7162
- lines.push("");
7163
- if (llmResult) {
7164
- lines.push("## LLM \u7CBE\u63D0(\u542B test cases \u6307\u4EE4,\u4EBA\u5BA1)");
7165
- lines.push(llmResult);
7166
- } else {
7167
- lines.push("## \u6539\u8FDB\u6307\u4EE4(\u7ED9 AI,\u65E0 LLM \u964D\u7EA7\u7EAF\u6B63\u5219)");
7168
- lines.push("\u8BF7\u57FA\u4E8E\u4EE5\u4E0A\u5019\u9009,\u6311 1-2 \u4E2A\u6700\u9891\u7E41\u7684,\u751F\u6210 SKILL.md \u8349\u7A3F:");
7169
- lines.push('- description \u8BF4\u660E"\u4F55\u65F6\u7528"');
7170
- lines.push("- body \u542B\u8BE5\u547D\u4EE4/\u5DE5\u4F5C\u6D41");
7171
- lines.push("- **\u542B test cases(Happy/Edge/Error)**");
7172
- lines.push("- \u4E0D\u8981\u63D0\u8BAE\u5DF2\u88C5 skill \u91CD\u590D\u7684");
7173
- }
7174
- lines.push("");
7175
- lines.push("\u751F\u6210\u8349\u7A3F,\u6211\u5BA1\u8FC7\u540E\u4E22\u8FDB .claude/skills/(\u4E0D\u81EA\u52A8\u521B\u5EFA)\u3002");
7176
- return lines.join("\n");
7040
+ const candidates = [...skeletonCount.entries()].filter(([, info]) => info.count >= 3).sort((a, b) => b[1].count - a[1].count).map(([skeleton, info]) => ({ skeleton, count: info.count, examples: info.examples }));
7041
+ const tool_sequences = [...seqCount.entries()].filter(([seq, count]) => count >= 3 && !isSeqNoise(seq)).sort((a, b) => b[1] - a[1]).slice(0, 10).map(([sequence, count]) => ({ sequence, count }));
7042
+ return { candidates, tool_sequences };
7177
7043
  }
7178
7044
 
7179
7045
  // package.json
7180
7046
  var package_default = {
7181
7047
  name: "left-skills",
7182
- version: "0.7.0",
7048
+ version: "0.9.0",
7183
7049
  description: "\u7ED9 AI \u7528\u7684 skill \u751F\u547D\u5468\u671F\u7BA1\u7406\u5DE5\u5177 \u2014 MVP: skill \u8C03\u7528\u4F7F\u7528\u7EDF\u8BA1",
7184
7050
  bin: {
7185
7051
  "left-skills": "./dist/cli.js"
@@ -7223,7 +7089,7 @@ var package_default = {
7223
7089
 
7224
7090
  // src/cli.ts
7225
7091
  var program2 = new Command();
7226
- program2.name("left-skills").description("\u7ED9 AI \u7528\u7684 skill \u751F\u547D\u5468\u671F\u7BA1\u7406\u5DE5\u5177 \u2014 skill \u8C03\u7528\u4F7F\u7528\u7EDF\u8BA1").version(package_default.version);
7092
+ program2.name("left-skills").description("\u7ED9 AI \u7528\u7684 skill \u751F\u547D\u5468\u671F\u7BA1\u7406\u5DE5\u5177(\u6570\u636E\u5DE5\u5177\u7BB1,skill \u5165\u53E3 + AI \u5206\u6790)").version(package_default.version);
7227
7093
  program2.command("usage").description("skill \u8C03\u7528\u4F7F\u7528\u62A5\u544A").option("--json", "\u8F93\u51FA JSON(AI \u7528)").option("--since <days>", "\u65F6\u95F4\u7A97\u53E3(\u5929,\u9ED8\u8BA4 30)", "30").action((opts) => {
7228
7094
  const since = parseInt(opts.since, 10) || 30;
7229
7095
  const report = buildReport(since);
@@ -7233,18 +7099,34 @@ program2.command("usage").description("skill \u8C03\u7528\u4F7F\u7528\u62A5\u544
7233
7099
  console.log(formatHuman(report));
7234
7100
  }
7235
7101
  });
7236
- program2.command("lint").description("\u9759\u6001\u8D28\u91CF\u68C0\u67E5 SKILL.md(\u5BF9\u9F50 skills-ref + \u8865\u6DF1\u5EA6,0-100 \u5206)").action(() => {
7237
- const results = lintAll();
7238
- console.log(formatLintHuman(results));
7102
+ program2.command("scan").description("\u626B\u4F1A\u8BDD\u627E\u91CD\u590D Bash \u547D\u4EE4 + tool \u5E8F\u5217(\u6570\u636E\u91C7\u96C6,\u4E0D LLM)").option("--json", "\u8F93\u51FA JSON(AI \u7528)").option("--since <days>", "\u65F6\u95F4\u7A97\u53E3(\u5929,\u9ED8\u8BA4 30)", "30").action((opts) => {
7103
+ const since = parseInt(opts.since, 10) || 30;
7104
+ const result = scan(since);
7105
+ if (opts.json) {
7106
+ console.log(JSON.stringify(result));
7107
+ } else {
7108
+ console.log(`scan \u62A5\u544A(${result.candidates.length} \u5019\u9009 + ${result.tool_sequences.length} \u5E8F\u5217)`);
7109
+ for (const c of result.candidates) console.log(` ${c.count} ${c.skeleton}`);
7110
+ for (const s of result.tool_sequences) console.log(` ${s.count} ${s.sequence}`);
7111
+ }
7239
7112
  });
7240
- program2.command("evolve <skill>").description("\u6536\u96C6 usage+lint \u4FE1\u53F7,\u8F93\u51FA\u6539\u8FDB prompt(\u7ED9 AI,\u4EBA\u5BA1,\u4E0D\u81EA\u52A8\u6539)").action((skill) => {
7241
- console.log(evolvePrompt(skill));
7113
+ program2.command("list-skills").description("\u5217\u5DF2\u88C5 skill(.claude/skills + .codex/skills)").option("--json", "\u8F93\u51FA JSON(AI \u7528)").action((opts) => {
7114
+ const skills = listInstalledSkills();
7115
+ if (opts.json) {
7116
+ console.log(JSON.stringify({ skills }));
7117
+ } else {
7118
+ for (const s of skills) console.log(s);
7119
+ }
7242
7120
  });
7243
- program2.command("inspire").description("\u626B\u4F1A\u8BDD\u627E\u91CD\u590D\u547D\u4EE4,\u63D0\u8BAE\u5199 skill(hybrid \u6B63\u5219+LLM,\u7ED9 AI,\u4EBA\u5BA1,\u4E0D\u81EA\u52A8\u521B\u5EFA)").option("--since <days>", "\u65F6\u95F4\u7A97\u53E3(\u5929,\u9ED8\u8BA4 30)", "30").action(async (opts) => {
7244
- const since = parseInt(opts.since, 10) || 30;
7245
- console.log(await inspirePrompt(since));
7121
+ program2.command("lint").description("\u9759\u6001\u8D28\u91CF\u68C0\u67E5 SKILL.md(0-100 \u5206)").option("--json", "\u8F93\u51FA JSON(AI \u7528)").action((opts) => {
7122
+ const results = lintAll();
7123
+ if (opts.json) {
7124
+ console.log(formatLintJson(results));
7125
+ } else {
7126
+ console.log(formatLintHuman(results));
7127
+ }
7246
7128
  });
7247
- 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) => {
7129
+ program2.command("report").description("\u5BFC\u51FA usage \u62A5\u544A markdown(\u53EF > report.md)").option("--markdown", "\u8F93\u51FA markdown(\u9ED8\u8BA4)").option("--since <days>", "\u65F6\u95F4\u7A97\u53E3(\u5929,\u9ED8\u8BA4 30)", "30").action((opts) => {
7248
7130
  const since = parseInt(opts.since, 10) || 30;
7249
7131
  const report = buildReport(since);
7250
7132
  console.log(formatMarkdown(report));
@@ -7257,22 +7139,23 @@ program2.command("doctor").description("\u8BCA\u65AD left-skills \u5B89\u88C5/ho
7257
7139
  if (r.fix) console.log(` \u2192 \u4FEE\u590D: ${r.fix}`);
7258
7140
  }
7259
7141
  });
7260
- 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) => {
7142
+ program2.command("install").description("\u8F93\u51FA hook \u7247\u6BB5(\u9ED8\u8BA4)\u6216 --write \u914D hook + \u653E SKILL.md").option("--write", "\u81EA\u52A8\u5199 hook + \u653E SKILL.md(\u5907\u4EFD .bak)", false).action((opts) => {
7261
7143
  if (opts.write) {
7262
7144
  const path = globalSettingsPath();
7263
7145
  writeHooksToSettings(path);
7264
- console.log(`\u2713 hook \u5DF2\u5199\u5165 ${path}(\u5DF2\u5907\u4EFD .bak)`);
7265
- console.log(" \u6253 /skill \u6216 AI \u8C03 skill \u4F1A\u81EA\u52A8\u8BB0\u5F55,\u8DD1 left-skills usage \u770B\u62A5\u544A");
7146
+ writeSkillWrapper();
7147
+ console.log(`\u2713 hook \u5DF2\u5199\u5165 ${path}(\u5907\u4EFD .bak)`);
7148
+ console.log(`\u2713 SKILL.md \u5DF2\u653E ~/.claude/skills/left-skills/(/left-skills slash \u89E6\u53D1)`);
7266
7149
  } else {
7267
7150
  console.log(JSON.stringify(hookSnippet(), null, 2));
7268
- console.log("\n# \u628A\u4E0A\u9762\u7247\u6BB5\u52A0\u8FDB ~/.claude/settings.json \u7684 hooks \u5B57\u6BB5,\u6216\u8DD1 left-skills install --write \u81EA\u52A8\u5199");
7269
7151
  }
7270
7152
  });
7271
- program2.command("uninstall").description("\u5220 ~/.claude/settings.json \u7684 left-skills hook(\u5E72\u51C0\u5378\u8F7D,\u5907\u4EFD .bak)").action(() => {
7153
+ program2.command("uninstall").description("\u5220 hook + SKILL.md(\u5E72\u51C0\u5378\u8F7D)").action(() => {
7272
7154
  const path = globalSettingsPath();
7273
7155
  removeHooksFromSettings(path);
7274
- console.log(`\u2713 left-skills hook \u5DF2\u4ECE ${path} \u5220\u9664(\u5DF2\u5907\u4EFD .bak)`);
7275
- console.log(" \u518D\u8DD1 npm uninstall -g left-skills \u5378\u8F7D binary");
7156
+ removeSkillWrapper();
7157
+ console.log(`\u2713 hook \u5DF2\u5220 ${path}(\u5907\u4EFD .bak)`);
7158
+ console.log(`\u2713 SKILL.md \u5DF2\u5220`);
7276
7159
  });
7277
7160
  program2.command("hook <event>").description("hook \u5165\u53E3(\u8BFB stdin payload)").action(async (event) => {
7278
7161
  const payload = await readStdinPayload();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "left-skills",
3
- "version": "0.7.0",
3
+ "version": "0.9.0",
4
4
  "description": "给 AI 用的 skill 生命周期管理工具 — MVP: skill 调用使用统计",
5
5
  "bin": {
6
6
  "left-skills": "./dist/cli.js"