skill-forge-hub 1.1.0 → 1.2.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 (107) hide show
  1. package/CHANGELOG.en.md +10 -0
  2. package/CHANGELOG.md +10 -0
  3. package/README.en.md +3 -1
  4. package/README.md +3 -1
  5. package/SKILLS_CATALOG.md +119 -0
  6. package/annual-report/SKILL.md +3 -3
  7. package/annual-report/resources/reference.html +2 -2
  8. package/annual-report/resources/skillforge-guide.html +1 -0
  9. package/annual-report/resources/skillforge-guide.txt +1 -0
  10. package/annual-report/user-config.md +1 -1
  11. package/article-to-html/SKILL.md +1 -1
  12. package/article-to-html/resources/skillforge-guide.html +1 -0
  13. package/article-to-html/resources/skillforge-guide.txt +1 -0
  14. package/article-to-html/user-config.md +1 -1
  15. package/cli.js +54 -14
  16. package/commit-heatmap/SKILL.md +2 -2
  17. package/commit-heatmap/resources/skillforge-guide.html +1 -0
  18. package/commit-heatmap/resources/skillforge-guide.txt +1 -0
  19. package/commit-heatmap/user-config.md +1 -1
  20. package/daily-report/SKILL.md +2 -2
  21. package/daily-report/resources/skillforge-guide.html +1 -0
  22. package/daily-report/resources/skillforge-guide.txt +1 -0
  23. package/daily-report/user-config.md +1 -1
  24. package/data-prepper/SKILL.md +2 -2
  25. package/data-prepper/resources/skillforge-guide.html +1 -0
  26. package/data-prepper/resources/skillforge-guide.txt +1 -0
  27. package/data-prepper/user-config.md +1 -1
  28. package/decision-log/SKILL.md +2 -2
  29. package/decision-log/resources/skillforge-guide.html +1 -0
  30. package/decision-log/resources/skillforge-guide.txt +1 -0
  31. package/decision-log/user-config.md +1 -1
  32. package/deck-stage/SKILL.md +3 -10
  33. package/deck-stage/resources/skillforge-guide.html +1 -0
  34. package/deck-stage/resources/skillforge-guide.txt +1 -0
  35. package/docs/articles/ai-daily-report-correct-pose.md +39 -0
  36. package/docs/articles/ai-prompt-management-skill-pouch.md +37 -0
  37. package/docs/directory-submission.md +106 -0
  38. package/docs/install.en.md +3 -3
  39. package/docs/install.md +3 -3
  40. package/docs/usage-examples.en.md +1 -1
  41. package/docs/usage-examples.md +1 -1
  42. package/magic-deck/SKILL.md +4 -5
  43. package/magic-deck/resources/skillforge-guide.html +1 -0
  44. package/magic-deck/resources/skillforge-guide.txt +1 -0
  45. package/magic-deck/user-config.md +1 -1
  46. package/meeting-minutes/SKILL.md +1 -1
  47. package/meeting-minutes/resources/skillforge-guide.html +1 -0
  48. package/meeting-minutes/resources/skillforge-guide.txt +1 -0
  49. package/meeting-minutes/user-config.md +1 -1
  50. package/meta-forge/SKILL.md +2 -2
  51. package/meta-forge/resources/skillforge-guide.html +1 -0
  52. package/meta-forge/resources/skillforge-guide.txt +1 -0
  53. package/meta-forge/user-config.md +1 -1
  54. package/monthly-report/SKILL.md +1 -1
  55. package/monthly-report/resources/skillforge-guide.html +1 -0
  56. package/monthly-report/resources/skillforge-guide.txt +1 -0
  57. package/monthly-report/user-config.md +1 -1
  58. package/one-pager/SKILL.md +20 -8
  59. package/one-pager/resources/design-process.md +117 -0
  60. package/one-pager/resources/generate.md +19 -7
  61. package/one-pager/resources/html-rules.md +55 -10
  62. package/one-pager/resources/reference.html +1192 -111
  63. package/one-pager/resources/skillforge-guide.html +1 -0
  64. package/one-pager/resources/skillforge-guide.txt +1 -0
  65. package/one-pager/resources/usage-guide.md +18 -4
  66. package/one-pager/resources/user-template-example.html +63 -0
  67. package/one-pager/user-config.md +7 -4
  68. package/package.json +3 -2
  69. package/prompt-pouch/SKILL.md +4 -15
  70. package/prompt-pouch/resources/reference.html +180 -42
  71. package/prompt-pouch/resources/skillforge-guide.html +1 -0
  72. package/prompt-pouch/resources/skillforge-guide.txt +1 -0
  73. package/prompt-pouch/resources/usage-guide.md +3 -3
  74. package/quarterly-report/SKILL.md +1 -1
  75. package/quarterly-report/resources/skillforge-guide.html +1 -0
  76. package/quarterly-report/resources/skillforge-guide.txt +1 -0
  77. package/quarterly-report/user-config.md +1 -1
  78. package/release-notes/SKILL.md +2 -2
  79. package/release-notes/resources/skillforge-guide.html +1 -0
  80. package/release-notes/resources/skillforge-guide.txt +1 -0
  81. package/release-notes/user-config.md +1 -1
  82. package/skill-doctor/SKILL.md +2 -2
  83. package/skill-doctor/resources/skillforge-guide.html +1 -0
  84. package/skill-doctor/resources/skillforge-guide.txt +1 -0
  85. package/skill-doctor/user-config.md +1 -1
  86. package/spec-doc2spec/SKILL.md +3 -9
  87. package/spec-doc2spec/resources/skillforge-guide.html +1 -0
  88. package/spec-doc2spec/resources/skillforge-guide.txt +1 -0
  89. package/spec-doc2spec/user-config.md +1 -1
  90. package/spec-spec2doc/SKILL.md +3 -12
  91. package/spec-spec2doc/resources/skillforge-guide.html +1 -0
  92. package/spec-spec2doc/resources/skillforge-guide.txt +1 -0
  93. package/spec-spec2doc/user-config.md +1 -1
  94. package/team-report-collector/SKILL.md +2 -2
  95. package/team-report-collector/resources/skillforge-guide.html +1 -0
  96. package/team-report-collector/resources/skillforge-guide.txt +1 -0
  97. package/team-report-collector/user-config.md +1 -1
  98. package/template/SKILL.md +2 -2
  99. package/template/user-config.md +1 -1
  100. package/twin-docs/SKILL.md +2 -2
  101. package/twin-docs/resources/skillforge-guide.html +1 -0
  102. package/twin-docs/resources/skillforge-guide.txt +1 -0
  103. package/twin-docs/user-config.md +1 -1
  104. package/weekly-report/SKILL.md +2 -2
  105. package/weekly-report/resources/skillforge-guide.html +1 -0
  106. package/weekly-report/resources/skillforge-guide.txt +1 -0
  107. package/weekly-report/user-config.md +1 -1
@@ -3,7 +3,7 @@
3
3
  <head>
4
4
  <meta charset="UTF-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <meta name="sf-template" content="prompt-pouch@2">
6
+ <meta name="sf-template" content="prompt-pouch@9">
7
7
  <meta name="pouch-id" content="REF-POUCH-0000">
8
8
  <title>锦囊 Prompt Pouch — 提示词管理台</title>
9
9
  <style>
@@ -200,12 +200,13 @@ header{
200
200
  /* 侧栏 */
201
201
  aside{ width:200px; flex:0 0 auto;
202
202
  display:flex; flex-direction:column;
203
- position:sticky; top:80px; align-self:flex-start;
204
- height:calc(100vh - 100px);
203
+ align-self:flex-start; /* 自然流式随页面整体滚动:说明卡完整展示,页面滚到底即可读完 */
205
204
  }
206
- /* 侧栏分组滚动区:收藏 + 分组局部滚动,分组下方紧跟「+ 新建分组」,滚动流末尾附「锦囊用法」说明——滚动区占满「全部」与侧栏底缘之间的剩余屏幕高度(最大占满屏幕),说明被顶出首屏(滚到底可见);窄滚动条 */
205
+ /* 侧栏分组滚动区:收藏 + 分组局部滚动——滚动区最大高度 = 视口高度减去顶栏、「全部」行与底部「锦囊用法」说明卡(完整展示不裁剪),分组再多说明卡也完整可见;窄滚动条 */
207
206
  .nav-scroll{
208
- flex:1 1 auto; min-height:0; overflow-y:auto;
207
+ flex:0 0 auto; min-height:0;
208
+ max-height:calc(100vh - 360px);
209
+ overflow-y:auto;
209
210
  margin:0 -4px 0 0; padding-right:4px;
210
211
  scrollbar-width:thin; scrollbar-color:var(--border) transparent;
211
212
  }
@@ -231,8 +232,8 @@ aside{ width:200px; flex:0 0 auto;
231
232
  }
232
233
  .nav-item.on .cnt{ color:var(--gold); background:var(--gold-dim); }
233
234
  .side-tip{
234
- flex:0 0 auto;
235
- margin:16px 4px 0 0; padding:12px 14px; border-radius:10px; /* margin-right 补偿 nav-scroll 负 margin(-4px)的右侧裁切,虚线边框完整不裁;滚动流末尾,分组多时被顶出首屏 */
235
+ flex:0 0 auto; /* 完整展示不裁剪:分组滚动区为其留出空间,侧栏随页面整体滚动即可读完 */
236
+ margin:12px 0 0 0; padding:12px 14px 14px; border-radius:10px;
236
237
  border:1px dashed var(--border); font-size:11px; line-height:1.9; color:var(--muted);
237
238
  }
238
239
  .side-tip b{ color:var(--gold); font-weight:700; }
@@ -246,13 +247,38 @@ main{ flex:1; min-width:0; }
246
247
  font-weight:900;
247
248
  }
248
249
  .bar .total{ font-size:12px; color:var(--muted); }
249
- .sort-box{ margin-left:auto; display:flex; align-items:center; gap:6px; font-size:11px; color:var(--muted); }
250
- .sort-box select{
250
+ .sort-box{ margin-left:auto; display:flex; align-items:center; gap:6px; font-size:11px; color:var(--muted); position:relative; }
251
+ .sort-sel{
252
+ display:flex; align-items:center; gap:7px;
251
253
  background:var(--panel2); border:1px solid var(--border); color:var(--text);
252
254
  font-family:inherit; font-size:12px; letter-spacing:1px;
253
- border-radius:999px; padding:5px 12px; outline:none; cursor:pointer;
254
- }
255
- .sort-box select:focus{ border-color:var(--gold); }
255
+ border-radius:999px; padding:5px 13px; outline:none; cursor:pointer;
256
+ transition:border-color .15s ease;
257
+ }
258
+ .sort-sel:hover,.sort-sel.open{ border-color:var(--gold); }
259
+ .sort-arrow{ font-size:9px; color:var(--gold); line-height:1; transition:transform .2s ease; }
260
+ .sort-sel.open .sort-arrow{ transform:rotate(180deg); }
261
+ .sort-menu{
262
+ position:absolute; top:calc(100% + 6px); right:0; z-index:130;
263
+ display:flex; flex-direction:column; gap:2px; min-width:136px;
264
+ background:var(--panel2); border:1px solid var(--border); border-radius:12px;
265
+ padding:5px; box-shadow:0 12px 32px rgba(0,0,0,.45);
266
+ animation:sortMenuIn .16s ease;
267
+ }
268
+ .sort-menu[hidden]{ display:none; }
269
+ @keyframes sortMenuIn{ from{ opacity:0; transform:translateY(-4px); } to{ opacity:1; transform:none; } }
270
+ .sort-opt{
271
+ display:flex; align-items:center; gap:8px; width:100%;
272
+ background:transparent; border:none; border-radius:8px;
273
+ color:var(--text); font-family:inherit; font-size:12px; letter-spacing:1px;
274
+ padding:7px 10px; cursor:pointer; text-align:left;
275
+ transition:background .12s ease,color .12s ease;
276
+ }
277
+ .sort-opt:hover{ background:var(--gold-dim); color:var(--gold); }
278
+ .sort-check{ width:14px; font-size:11px; color:var(--gold); opacity:0; flex:0 0 auto; }
279
+ .sort-opt.on{ color:var(--gold); font-weight:600; }
280
+ .sort-opt.on .sort-check{ opacity:1; }
281
+ [data-theme="light"] .sort-menu{ box-shadow:0 10px 28px rgba(30,31,34,.16); }
256
282
  .sort-status{
257
283
  font-size:12px; color:var(--muted);
258
284
  display:flex; align-items:center; gap:10px;
@@ -521,9 +547,10 @@ footer.open .ht-arrow{ transform:rotate(180deg); }
521
547
  }
522
548
  /* 页面直写:侧栏分组管理 / 提示词表单 / toast */
523
549
  .nav-add{
524
- display:flex; align-items:center; justify-content:center; gap:6px;
525
- margin-top:10px; padding:10px 14px; border-radius:10px; cursor:pointer;
526
- font-size:13px; letter-spacing:1px; color:var(--gold);
550
+ flex:0 0 auto; /* 「全部」行右侧迷你 + 图标(新建分组),行内不占独立高度 */
551
+ width:22px; height:22px; margin-left:6px;
552
+ display:flex; align-items:center; justify-content:center;
553
+ border-radius:6px; cursor:pointer; font-size:14px; line-height:1; color:var(--gold);
527
554
  border:1px dashed var(--border); background:var(--gold-dim);
528
555
  transition:all .18s ease; user-select:none;
529
556
  }
@@ -636,13 +663,16 @@ footer.open .ht-arrow{ transform:rotate(180deg); }
636
663
  <span class="tag-filter" id="tagFilterChip" style="display:none"><span class="tf-ico">🏷</span><b id="tagFilterName"></b><button class="tf-x" id="tagFilterClear" title="清除标签过滤">✕</button></span>
637
664
  <button class="btn primary" id="btnAddItem">+ 新增提示词</button>
638
665
  <div class="sort-box">
639
- <label for="sortSel">排序</label>
640
- <select id="sortSel" title="切换卡片排序方式">
641
- <option value="added">最新添加</option>
642
- <option value="recent">最近使用</option>
643
- <option value="freq">使用频率</option>
644
- <option value="manual">手动顺序</option>
645
- </select>
666
+ <label>排序</label>
667
+ <button class="sort-sel" id="sortSel" type="button" title="切换卡片排序方式">
668
+ <span class="sort-sel-txt" id="sortTxt">最新添加</span><span class="sort-arrow">▾</span>
669
+ </button>
670
+ <div class="sort-menu" id="sortMenu" hidden>
671
+ <button class="sort-opt on" data-mode="added" type="button"><span class="sort-check">✓</span>最新添加</button>
672
+ <button class="sort-opt" data-mode="recent" type="button"><span class="sort-check">✓</span>最近使用</button>
673
+ <button class="sort-opt" data-mode="freq" type="button"><span class="sort-check">✓</span>使用频率</button>
674
+ <button class="sort-opt" data-mode="manual" type="button"><span class="sort-check">✓</span>手动顺序</button>
675
+ </div>
646
676
  </div>
647
677
  <div class="sort-status" id="sortStatus">
648
678
  <button class="btn" id="btnSortCmd" disabled>📋 复制变更指令</button>
@@ -812,6 +842,9 @@ footer.open .ht-arrow{ transform:rotate(180deg); }
812
842
  var searchClear = document.getElementById('searchClear');
813
843
  var sortStatus = document.getElementById('sortStatus');
814
844
  var sortSel = document.getElementById('sortSel');
845
+ var sortMenu = document.getElementById('sortMenu');
846
+ var sortTxt = document.getElementById('sortTxt');
847
+ var SORT_LABELS = { added:'最新添加', recent:'最近使用', freq:'使用频率', manual:'手动顺序' };
815
848
  var btnSortCmd = document.getElementById('btnSortCmd');
816
849
  var btnMarkSaved = document.getElementById('btnMarkSaved');
817
850
  var bkStatus = document.getElementById('bkStatus');
@@ -944,16 +977,15 @@ footer.open .ht-arrow{ transform:rotate(180deg); }
944
977
  var html = '';
945
978
  var all = state.data.prompts.length;
946
979
  var favN = state.data.prompts.filter(function(p){ return p.favorite; }).length;
947
- html += '<div class="nav-item' + (state.curGroup==='all'?' on':'') + '" data-g="all"><span class="icon">🗂️</span>全部<span class="cnt">' + all + '</span></div>';
980
+ html += '<div class="nav-item' + (state.curGroup==='all'?' on':'') + '" data-g="all"><span class="icon">🗂️</span>全部<span class="cnt">' + all + '</span><span class="nav-add" id="navAdd" title="新建分组">+</span></div>';
948
981
  html += '<div class="nav-scroll">';
949
982
  html += '<div class="nav-item' + (state.curGroup==='fav'?' on':'') + '" data-g="fav"><span class="icon">⭐</span>收藏<span class="cnt">' + favN + '</span></div>';
950
983
  state.data.groups.forEach(function(g){
951
984
  var n = state.data.prompts.filter(function(p){ return p.group === g.id; }).length;
952
985
  html += '<div class="nav-item' + (state.curGroup===g.id?' on':'') + '" data-g="' + g.id + '" draggable="true" title="拖拽调整分组顺序"><span class="icon">' + g.icon + '</span><span class="g-name">' + escapeHtml(g.name) + '</span><span class="cnt">' + n + '</span><span class="g-ops"><button class="g-edit" title="编辑分组">✎</button><button class="g-del" title="删除分组">🗑</button></span></div>';
953
986
  });
954
- html += '<div class="nav-add" id="navAdd">+ 新建分组</div>';
955
- html += '<div class="side-tip"><b>锦囊用法</b><br>· 页面直接增删改:+ 新建分组 / + 新增提示词 / ✎ 编辑 / 🗑 删除(授权目录即写即存)<br>· 也可对 AI 说「把 XX 加到写作输出分组」<br>· 顶栏切换排序;⇄ 移组、拖卡片排序自动保存;拖分组条调整分组顺序<br>· 点卡片标签按标签筛选(再点取消);标签悬停 ✕ 删 / + 加;⤓ 导出 / ⤒ 导入 JSON 备份<br>· ⭐ 收藏常用 · 复制次数在卡片右上角</div>';
956
987
  html += '</div>';
988
+ html += '<div class="side-tip"><b>锦囊用法</b><br>· 页面直接增删改:+ 新建分组 / + 新增提示词 / ✎ 编辑 / 🗑 删除(授权目录即写即存)<br>· 也可对 AI 说「把 XX 加到写作输出分组」<br>· 顶栏切换排序;⇄ 移组、拖卡片排序自动保存;拖分组条调整分组顺序<br>· 点卡片标签按标签筛选(再点取消);标签悬停 ✕ 删 / + 加;⤓ 导出 / ⤒ 导入 JSON 备份<br>· ⭐ 收藏常用 · 复制次数在卡片右上角</div>';
957
989
  sideNav.innerHTML = html;
958
990
  sideNav.querySelectorAll('.nav-item').forEach(function(el){
959
991
  el.addEventListener('click', function(){
@@ -1001,7 +1033,7 @@ footer.open .ht-arrow{ transform:rotate(180deg); }
1001
1033
  });
1002
1034
  });
1003
1035
  var addBtn = sideNav.querySelector('#navAdd');
1004
- if (addBtn) addBtn.addEventListener('click', function(){ openGroupForm(null); });
1036
+ if (addBtn) addBtn.addEventListener('click', function(e){ e.stopPropagation(); openGroupForm(null); });
1005
1037
  }
1006
1038
 
1007
1039
  function render(){
@@ -1198,7 +1230,12 @@ footer.open .ht-arrow{ transform:rotate(180deg); }
1198
1230
  persistUserState();
1199
1231
  scheduleBackup();
1200
1232
  }
1201
- function syncSortUI(){ if (sortSel) sortSel.value = state.sortMode; }
1233
+ function syncSortUI(){
1234
+ if (sortTxt) sortTxt.textContent = SORT_LABELS[state.sortMode] || SORT_LABELS.added;
1235
+ if (sortMenu) sortMenu.querySelectorAll('.sort-opt').forEach(function(o){
1236
+ o.classList.toggle('on', o.getAttribute('data-mode') === state.sortMode);
1237
+ });
1238
+ }
1202
1239
  function ensureManual(){
1203
1240
  if (state.sortMode === 'manual') return;
1204
1241
  state.sortMode = 'manual';
@@ -1279,10 +1316,30 @@ footer.open .ht-arrow{ transform:rotate(180deg); }
1279
1316
  if (!gpopEl.contains(e.target) && !onAnchor){ closeGpop(); }
1280
1317
  }
1281
1318
  });
1282
- sortSel.addEventListener('change', function(){
1283
- state.sortMode = sortSel.value;
1319
+ function toggleSortMenu(force){
1320
+ if (!sortMenu) return;
1321
+ var willOpen = (force !== undefined) ? force : sortMenu.hidden;
1322
+ sortMenu.hidden = !willOpen;
1323
+ sortSel.classList.toggle('open', willOpen);
1324
+ }
1325
+ sortSel.addEventListener('click', function(e){
1326
+ e.stopPropagation();
1327
+ toggleSortMenu();
1328
+ });
1329
+ sortMenu.addEventListener('click', function(e){
1330
+ var opt = e.target.closest('.sort-opt');
1331
+ if (!opt) return;
1332
+ state.sortMode = opt.getAttribute('data-mode');
1284
1333
  persistUserState();
1285
1334
  render();
1335
+ syncSortUI();
1336
+ toggleSortMenu(false);
1337
+ });
1338
+ document.addEventListener('click', function(e){
1339
+ if (!sortMenu.hidden){
1340
+ var onSel = e.target.closest && e.target.closest('#sortSel');
1341
+ if (!sortMenu.contains(e.target) && !onSel){ toggleSortMenu(false); }
1342
+ }
1286
1343
  });
1287
1344
 
1288
1345
  /* ---------- 模板插值({{变量}} / {变量}) ---------- */
@@ -1448,6 +1505,7 @@ footer.open .ht-arrow{ transform:rotate(180deg); }
1448
1505
  if (next === null) return false;
1449
1506
  return bkWrite('prompts.json', 'data', JSON.stringify(next, null, 2)).then(function(){
1450
1507
  state.data = next;
1508
+ syncMirror(next);
1451
1509
  applyUserState();
1452
1510
  state.dirty = false; state.moves = [];
1453
1511
  persistUserState(); refreshDirtyUI();
@@ -1484,6 +1542,7 @@ footer.open .ht-arrow{ transform:rotate(180deg); }
1484
1542
  });
1485
1543
  return bkWrite('prompts.json', 'data', JSON.stringify(latest, null, 2)).then(function(){
1486
1544
  state.data = latest;
1545
+ syncMirror(latest);
1487
1546
  applyUserState();
1488
1547
  state.dirty = false; state.moves = [];
1489
1548
  persistUserState(); refreshDirtyUI();
@@ -1868,6 +1927,7 @@ footer.open .ht-arrow{ transform:rotate(180deg); }
1868
1927
  if (e.key === 'Escape'){
1869
1928
  closeFillPanel();
1870
1929
  closeGpop();
1930
+ toggleSortMenu(false);
1871
1931
  closeItemForm();
1872
1932
  closeGroupForm();
1873
1933
  document.getElementById('bkMask').classList.remove('open');
@@ -2030,6 +2090,19 @@ if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(POUC
2030
2090
  });
2031
2091
  });
2032
2092
  }
2093
+ /* 数据镜像:localStorage 存全量数据快照(file:// 下 IndexedDB 偶发不可用时兜底恢复,键按 pouch-id 隔离) */
2094
+ function syncMirror(data){
2095
+ try{ localStorage.setItem(SF_NS + '-mirror', JSON.stringify(data)); }catch(e){}
2096
+ }
2097
+ function readMirror(){
2098
+ try{
2099
+ var raw = localStorage.getItem(SF_NS + '-mirror');
2100
+ if (!raw) return null;
2101
+ var d = JSON.parse(raw);
2102
+ if (d && d.groups && d.prompts) return d;
2103
+ }catch(e){}
2104
+ return null;
2105
+ }
2033
2106
  /* 备份文件读:backup/ 优先,读不到回退根目录旧文件(旧目录结构兼容) */
2034
2107
  function readBk(name){
2035
2108
  return bkRead(name, 'backup').then(function(text){
@@ -2122,6 +2195,7 @@ if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(POUC
2122
2195
  try{ localStorage.removeItem(SF_NS + '-bk-declined'); }catch(e){}
2123
2196
  idbSet('bkHandle', bkDirHandle);
2124
2197
  updateBkUI();
2198
+ reloadFromDirIfNeeded();
2125
2199
  return true;
2126
2200
  })
2127
2201
  .catch(function(){ pendingAuth = false; updateBkUI(); return false; });
@@ -2168,6 +2242,43 @@ if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(POUC
2168
2242
  pendingAuth = false;
2169
2243
  }).catch(function(){});
2170
2244
  }
2245
+ /* file:// 直开补洞:fetch 被浏览器拦截读不到数据文件时,授权目录后直读 data/prompts.json 替换示例/快照 */
2246
+ function reloadFromDirIfNeeded(){
2247
+ if (!bkDirHandle || bootMode === 'live') return;
2248
+ readPouch().then(function(text){
2249
+ if (text === null) return;
2250
+ var data = JSON.parse(text);
2251
+ state.data = data;
2252
+ idbSet('lastData', data);
2253
+ syncMirror(data);
2254
+ bootMode = 'live';
2255
+ cacheBadge.style.display = 'none';
2256
+ applyUserState();
2257
+ renderNav(); render();
2258
+ toast('✓ 已直读 data/prompts.json,你的分组与提示词已载入');
2259
+ }).catch(function(){});
2260
+ }
2261
+ /* 「🔓 恢复数据」:已授权过目录但刷新后权限需重新确认(file:// 权限不跨会话持久)→ 一键 requestPermission 后直读,免重新选目录 */
2262
+ function restoreAuth(){
2263
+ if (!bkDirHandle || typeof bkDirHandle.requestPermission !== 'function') return;
2264
+ bkDirHandle.requestPermission({ mode: 'readwrite' }).then(function(st){
2265
+ if (st === 'granted'){
2266
+ pendingAuth = false; updateBkUI();
2267
+ reloadFromDirIfNeeded();
2268
+ } else {
2269
+ toast('未获授权:点「💾 授权目录」重新选择目录', true);
2270
+ }
2271
+ }).catch(function(){ toast('权限恢复失败,请重新授权目录', true); });
2272
+ }
2273
+ function updateBootBadge(){
2274
+ if (bootMode === 'live' || !bkDirHandle || !bootRendered) return;
2275
+ var link = ' <a id="bootRestore" style="cursor:pointer;text-decoration:underline">🔓 恢复数据</a>';
2276
+ cacheBadge.innerHTML = (bootMode === 'cache'
2277
+ ? '⚠️ 快照模式:当前显示上次快照,目录权限需确认后直读最新数据'
2278
+ : '📌 示例模式:检测到曾授权过目录,点击确认权限即可载入你的数据') + link;
2279
+ cacheBadge.style.display = '';
2280
+ document.getElementById('bootRestore').addEventListener('click', restoreAuth);
2281
+ }
2171
2282
  btnBkEnable.addEventListener('click', function(){
2172
2283
  if (bkDirHandle && !pendingAuth){
2173
2284
  writeBackupNow().then(function(ok){ toast(ok ? '✓ 备份完成 · backup/pouch-backup.json' : '备份失败,请重新授权目录', !ok); });
@@ -2179,7 +2290,7 @@ if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(POUC
2179
2290
  }
2180
2291
  if (bkDirHandle && pendingAuth){
2181
2292
  bkDirHandle.requestPermission({ mode: 'readwrite' }).then(function(st){
2182
- if (st === 'granted'){ pendingAuth = false; updateBkUI(); writeBackupNow(); }
2293
+ if (st === 'granted'){ pendingAuth = false; updateBkUI(); writeBackupNow(); reloadFromDirIfNeeded(); }
2183
2294
  else bkStatus.textContent = '未获授权,备份暂停';
2184
2295
  }).catch(function(){});
2185
2296
  return;
@@ -2191,6 +2302,7 @@ if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(POUC
2191
2302
  idbSet('bkHandle', bkDirHandle);
2192
2303
  updateBkUI();
2193
2304
  writeBackupNow();
2305
+ reloadFromDirIfNeeded();
2194
2306
  })
2195
2307
  .catch(function(){});
2196
2308
  });
@@ -2204,6 +2316,7 @@ if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(POUC
2204
2316
  idbSet('bkHandle', bkDirHandle);
2205
2317
  updateBkUI();
2206
2318
  writeBackupNow();
2319
+ reloadFromDirIfNeeded();
2207
2320
  })
2208
2321
  .catch(function(){});
2209
2322
  });
@@ -2267,6 +2380,7 @@ if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(POUC
2267
2380
  // 先把当前状态固化进备份(目录备份滚入 .prev.json / 快照环记一份),再应用所选备份
2268
2381
  (bkDirHandle ? writeBackupNow() : writeSnapNow()).then(function(){
2269
2382
  state.data = snap.data;
2383
+ syncMirror(snap.data);
2270
2384
  applyUserStateFrom(snap.userState || {});
2271
2385
  state.dirty = false; state.moves = [];
2272
2386
  persistUserState();
@@ -2303,7 +2417,7 @@ if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(POUC
2303
2417
  render();
2304
2418
  if (bootMode === 'cache') cacheBadge.style.display = '';
2305
2419
  if (bootMode === 'demo'){
2306
- cacheBadge.textContent = '📌 示例模式:未读到 data/prompts.json,当前展示系统自带示例提示词(file:// 直开或数据文件尚未生成)。经 HTTP 打开页面并生成数据文件后即为你的提示词,示例可随时删除。';
2420
+ cacheBadge.textContent = '📌 示例模式:未读到 data/prompts.json,当前展示系统自带示例。若你有自己的数据文件:点顶部「💾 授权目录」选 prompt-pouch.html 所在文件夹即可直读载入;或经 HTTP 打开页面(file:// 直开会被浏览器拦截文件读取)。';
2307
2421
  cacheBadge.style.display = '';
2308
2422
  }
2309
2423
  }
@@ -2319,26 +2433,49 @@ if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(POUC
2319
2433
  .then(function(data){
2320
2434
  state.data = data;
2321
2435
  idbSet('lastData', data);
2436
+ syncMirror(data);
2322
2437
  bootMode = 'live';
2323
2438
  firstRender();
2324
2439
  })
2325
2440
  .catch(function(){
2326
- idbGet('lastData').then(function(cached){
2327
- if (cached){
2328
- state.data = cached;
2329
- bootMode = 'cache';
2330
- firstRender();
2331
- } else {
2332
- state.data = DEFAULT_POUCH;
2333
- bootMode = 'demo';
2334
- firstRender();
2335
- }
2441
+ // file:// 场景:fetch 被浏览器拦截 → 已授权目录则直读 data/prompts.json(File System Access API)
2442
+ return dirHandleReady.then(function(){
2443
+ return readPouch().then(function(text){
2444
+ if (text !== null) return JSON.parse(text);
2445
+ throw new Error('no-fs');
2446
+ });
2447
+ }).then(function(data){
2448
+ state.data = data;
2449
+ idbSet('lastData', data);
2450
+ syncMirror(data);
2451
+ bootMode = 'live';
2452
+ firstRender();
2453
+ }).catch(function(){
2454
+ idbGet('lastData').then(function(cached){
2455
+ if (cached){
2456
+ state.data = cached;
2457
+ bootMode = 'cache';
2458
+ firstRender();
2459
+ } else {
2460
+ var mirror = readMirror();
2461
+ if (mirror){
2462
+ state.data = mirror;
2463
+ bootMode = 'cache';
2464
+ firstRender();
2465
+ } else {
2466
+ state.data = DEFAULT_POUCH;
2467
+ bootMode = 'demo';
2468
+ firstRender();
2469
+ }
2470
+ }
2471
+ });
2336
2472
  });
2337
2473
  });
2338
2474
  // 本地态并行:可用则应用收藏与排序并刷新备份区(不阻塞数据展示)
2475
+ var dirHandleReady = idbOpen().then(function(){ return loadDirHandle(); }).catch(function(){});
2339
2476
  idbOpen()
2340
2477
  .then(loadLocalState)
2341
- .then(loadDirHandle)
2478
+ .then(function(){ return dirHandleReady; })
2342
2479
  .catch(function(){})
2343
2480
  .then(function(){
2344
2481
  applyUserState();
@@ -2346,6 +2483,7 @@ if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(POUC
2346
2483
  refreshDirtyUI();
2347
2484
  if (bootRendered){ renderNav(); render(); }
2348
2485
  updateBkUI();
2486
+ updateBootBadge();
2349
2487
  });
2350
2488
  })();
2351
2489
 
@@ -361,6 +361,7 @@ footer{text-align:center;font-size:10px;color:var(--muted);letter-spacing:1px;pa
361
361
  <tr><td>查全部命令</td><td><span class="cmd-in">skill-forge --help</span></td></tr>
362
362
  <tr><td>问单个技能用法</td><td>直接问「XX 技能怎么用」,AI 会读该技能的使用说明输出</td></tr>
363
363
  <tr><td>问 SkillForge 本身</td><td>问「skillforge 怎么用」,AI 朗读本指引并打开排版页</td></tr>
364
+ <tr><td>查技能双语简介 / 实操文章</td><td><span class="cmd-in">SKILLS_CATALOG.md</span>(21 技能逐条中英简介)/ <span class="cmd-in">docs/articles/</span>(实操文章)/ <span class="cmd-in">docs/directory-submission.md</span>(目录收录素材)</td></tr>
364
365
  </table>
365
366
  <div class="faq">
366
367
  <div class="faq-title">常见问题</div>
@@ -122,6 +122,7 @@ skill-forge init --skills daily-report,weekly-report --yes
122
122
  | 查全部命令 | skill-forge --help |
123
123
  | 问单个技能用法 | 直接问「XX 技能怎么用」,AI 会读该技能的使用说明输出 |
124
124
  | 问 SkillForge 本身 | 问「skillforge 怎么用」,AI 朗读本指引并打开排版页 |
125
+ | 查技能双语简介 / 实操文章 | SKILLS_CATALOG.md(21 技能逐条中英简介)/ docs/articles/(实操文章)/ docs/directory-submission.md(目录收录素材) |
125
126
 
126
127
  ## 六、常见问题
127
128
 
@@ -4,13 +4,13 @@
4
4
 
5
5
  ## 怎么用
6
6
 
7
- 1. **打开页面**:对 AI 说「打开锦囊」即可(自动起服务并打开浏览器);手动方式:`cd <目录> && python3 -m http.server 8933`(Windows 用 `py -m http.server`),浏览器访问 `http://localhost:8933/prompt-pouch.html`(HTTP 方式才能读 data/prompts.json;file:// 直开时回退浏览器缓存快照,无快照则展示内置示例提示词(仅作示范),不推荐日常使用)
7
+ 1. **打开页面**:对 AI 说「打开锦囊」即可(自动起服务并打开浏览器);手动方式:`cd <目录> && python3 -m http.server 8933`(Windows 用 `py -m http.server`),浏览器访问 `http://localhost:8933/prompt-pouch.html`(HTTP 方式自动读 data/prompts.json;file:// 直开时浏览器会拦截文件读取——已授权目录则刷新自动直读 data/prompts.json,权限需重新确认时点徽标「🔓 恢复数据」一键续权(免重新选目录),读不到则回退本地快照/镜像、再回退内置示例,点页面「💾 授权目录」即可载入你的数据,但仍推荐 HTTP 打开)
8
8
  2. **添加提示词**:页面点「+ 新增提示词」填表保存(已授权目录即写即存);或对 AI 说「把 XX 加到锦囊的写作输出分组」(不说分组也行,AI 会列出现有分组让你选;分组不存在时会问你是否新建)
9
9
  3. **查看与修改**:卡片正文超长只显示前三行,点「👁 详情」弹窗看全文(超长限高滚动),弹窗内可直接「⧉ 复制」或「✎ 修改」,并附时间线——创建时间、上次修改时间(修改过才显示)、最近使用时间(用过才显示);卡片上也能直接点「✎」改完保存;或对 AI 说「修改锦囊里『日报速写』的内容」,改后刷新
10
10
  4. **删除**:页面点卡片「🗑」;或对 AI 说「删掉锦囊里『XX』这条」
11
11
  5. **排序与移组**:顶栏「排序」切换四种方式——最新添加(默认)/ 最近使用 / 使用频率 / 手动顺序;想手动排时拖动卡片或点 ▲▼(自动切回手动),点卡片 ⇄ 可把提示词移到其它分组;已授权目录时排序/移组 2 秒内自动保存,未授权时点「📋 复制变更指令」把指令发给 AI 落盘;复制过提示词后卡片右上角会显示「⧉ N 次 · 最近时间」(最近使用/使用频率排序即按复制统计);AI 落盘后回页面点「✓ 已落盘」清除未保存标记
12
12
  6. **收藏**:卡片右上角星标,收藏/取消收藏指令由 AI 同步到数据文件
13
- 7. **分组管理**:分组下方「+ 新建分组」(紧随分组列表);悬停分组名出现「✎ 改名 / 🗑 删除」(组内还有提示词时不允许删,先移出或删条目);重名分组会提示;新建/编辑分组时图标输入框下方有快捷 emoji 面板(点击即填入、选中高亮,输入框仍可手输/粘贴任意 emoji)
13
+ 7. **分组管理**:「全部」行右侧「+」小图标新建分组;悬停分组名出现「✎ 改名 / 🗑 删除」(组内还有提示词时不允许删,先移出或删条目);重名分组会提示;新建/编辑分组时图标输入框下方有快捷 emoji 面板(点击即填入、选中高亮,输入框仍可手输/粘贴任意 emoji)
14
14
  8. **标签全交互**:点卡片标签立即按标签筛选(顶栏出现「🏷 标签」胶囊,点 ✕ 或再点同标签取消,切分组自动清除,与搜索叠加);标签悬停出「✕」点它直接删除该标签;标签不满 3 个时悬停出「+」快速加标签(多个用逗号分隔)——已授权目录即写即存,未授权时操作指令复制给 AI 落盘
15
15
  9. **分组排序**:左侧分组条可直接拖拽调整顺序(已授权目录即写即存,未授权时指令自动复制给 AI 落盘)
16
16
  10. **导入 / 导出**:顶栏「⤓ 导出」把当前全部数据下载为 JSON 备份文件(文件名带时间戳);「⤒ 导入」选择 JSON 文件整库覆盖现有数据(导入前自动备份,可从「↩ 恢复」回滚;未授权目录时导出仍可用、导入需先授权)
@@ -22,7 +22,7 @@
22
22
  - **自动备份(默认开启)**:数据一有变更 2 秒内自动备份——首次变更会请求授权写入 prompt-pouch.html 所在目录(弹窗选同目录即可,选一次后浏览器记住),写 backup/pouch-backup.json 并滚动保留上一版 .prev.json;未授权或浏览器不支持时自动降级为本地快照(浏览器内保留最近 5 份),页内「↩ 恢复」均可一键回滚;顶栏「📂 换目录」可随时重新配置备份目录
23
23
  - **详情弹窗**:点卡片「👁 详情」弹出全文查看(超长内容限高 52vh 内滚动),带复制与修改入口,附时间线(创建 / 上次修改 / 最近使用);含 {变量} 的模板会在弹窗里标出变量清单,复制时自动进入填充面板
24
24
  - **页面直写(双笔)**:页面增删改直接写入 data/prompts.json(与自动备份共用同一目录授权);未授权时自动降级——生成操作指令复制到剪贴板,粘贴给 AI 落盘
25
- - **侧栏局部滚动**:分组较多时,左侧「收藏 + 分组列表」区域独立滚动(窄滚动条,滚动区最大占满屏幕剩余高度),「+ 新建分组」紧随分组流、「锦囊用法」滚到底可见,「全部」固定不滚;窄屏下自动恢复平铺排列
25
+ - **侧栏局部滚动**:分组较多时,左侧「收藏 + 分组列表」区域独立滚动(窄滚动条,滚动区最大高度 = 屏幕高度减去顶栏、「全部」行与底部说明卡),「+ 新建分组」在「全部」行右侧不随滚动、「全部」固定不滚;底部「锦囊用法」说明卡完整展示不裁剪,侧栏随页面整体滚动即可读完;窄屏下自动恢复平铺排列
26
26
  - **批量整理入囊**:把一堆文字/文件交给 AI,或说「把最近对话框内容整理进锦囊」——两段确认:先定条目(AI 默认智能打标——每条自动配 1-3 个语义标签,可改可删;含具体词的可泛化为 {变量} 插值,附改写示例)→ 再定分组(归组理由 + 装不下的推荐新分组名),都确认后一次性写入;条目少(≤3 条)两段合一
27
27
  - **标签全交互**:点标签即筛选(顶栏胶囊一键清除,再点取消,切分组自动清除,与搜索叠加);标签悬停「✕」删、「+」加(最多 3 个),授权目录即写即存,未授权指令交 AI
28
28
  - **导入 / 导出 JSON**:顶栏「⤓ 导出」一键下载全量数据为 JSON 备份文件(文件名带时间戳);「⤒ 导入」选择 JSON 文件整库覆盖(导入前自动备份、可恢复);未授权目录时导出仍可用、导入需先授权
@@ -34,7 +34,7 @@ disable-model-invocation: false
34
34
 
35
35
  ## 配置版本机制(升级保护)
36
36
 
37
- 本 skill 当前配置版本为 `3`(对应 user-config.md 中的 `CONFIG_VERSION`)。执行时若发现 user-config.md 的 CONFIG_VERSION 与本节声明的当前版本不一致:先将旧 user-config.md 备份为 `user-config.md.bak-<时间戳>`(同目录,绝不删除旧值)→ 按本节「当前版本变更记录」迁移字段(新增字段补默认值、改名字段按映射改写、删除字段的值保留在备份中并在对话提示用户手动确认)→ 更新 CONFIG_VERSION 为当前版本 → 对话中明确告知用户「检测到配置版本从 X 升级到 Y,旧配置已备份至 <路径>」。禁止在未声明版本变更的情况下静默修改配置语义。
37
+ 本 skill 当前配置版本为 `3`(对应 user-config.md 中的 `CONFIG_VERSION`)。执行时若发现 user-config.md 的 CONFIG_VERSION 与本节声明的当前版本不一致:先将旧 user-config.md 备份为 `user-config.bak-<时间戳>.md`(同目录,绝不删除旧值)→ 按本节「当前版本变更记录」迁移字段(新增字段补默认值、改名字段按映射改写、删除字段的值保留在备份中并在对话提示用户手动确认)→ 更新 CONFIG_VERSION 为当前版本 → 对话中明确告知用户「检测到配置版本从 X 升级到 Y,旧配置已备份至 <路径>」。禁止在未声明版本变更的情况下静默修改配置语义。
38
38
 
39
39
  **无法迁移项处理**(值域失效 / 语义漂移 / 数据不兼容):三情形——①旧值不在新值域(如旧模板/主题名已移除)②同名键含义变化(单位/枚举变更)③数据文件 schema 不兼容。三绝不:绝不静默(无法迁移项停下问,不猜值)、绝不丢失(旧值永远在 .bak-时间戳)、一次说清(升级报告三段式:⚠️ 版本与备份路径 → ✅ 已自动迁移清单 → 🟡 无法迁移项逐条附原因+影响+建议替代);随后 AskUserQuestion 三选项(A 用建议值 / B 用户给新值 / C 保持旧配置——不更新 CONFIG_VERSION,每次执行再提醒);报告必附回滚一句话。
40
40
 
@@ -361,6 +361,7 @@ footer{text-align:center;font-size:10px;color:var(--muted);letter-spacing:1px;pa
361
361
  <tr><td>查全部命令</td><td><span class="cmd-in">skill-forge --help</span></td></tr>
362
362
  <tr><td>问单个技能用法</td><td>直接问「XX 技能怎么用」,AI 会读该技能的使用说明输出</td></tr>
363
363
  <tr><td>问 SkillForge 本身</td><td>问「skillforge 怎么用」,AI 朗读本指引并打开排版页</td></tr>
364
+ <tr><td>查技能双语简介 / 实操文章</td><td><span class="cmd-in">SKILLS_CATALOG.md</span>(21 技能逐条中英简介)/ <span class="cmd-in">docs/articles/</span>(实操文章)/ <span class="cmd-in">docs/directory-submission.md</span>(目录收录素材)</td></tr>
364
365
  </table>
365
366
  <div class="faq">
366
367
  <div class="faq-title">常见问题</div>
@@ -122,6 +122,7 @@ skill-forge init --skills daily-report,weekly-report --yes
122
122
  | 查全部命令 | skill-forge --help |
123
123
  | 问单个技能用法 | 直接问「XX 技能怎么用」,AI 会读该技能的使用说明输出 |
124
124
  | 问 SkillForge 本身 | 问「skillforge 怎么用」,AI 朗读本指引并打开排版页 |
125
+ | 查技能双语简介 / 实操文章 | SKILLS_CATALOG.md(21 技能逐条中英简介)/ docs/articles/(实操文章)/ docs/directory-submission.md(目录收录素材) |
125
126
 
126
127
  ## 六、常见问题
127
128
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  > 你的个人配置全放在这里。skill-forge 升级技能时官方只更新 SKILL.md 与 resources/,
4
4
  > 本文件始终保留你的版本——落盘目录、格式、角色画像、自定义模板放心改。
5
- > 官方升级若带来新配置点,会另存 `user-config.md.upstream` 对照文件到同目录,
5
+ > 官方升级若带来新配置点,会另存 `user-config.upstream.md` 对照文件到同目录,
6
6
  > 你按需查看并手动合并新配置点到本文件。
7
7
 
8
8
 
@@ -20,13 +20,13 @@ disable-model-invocation: false
20
20
 
21
21
  ## ⚙️ 配置与自定义
22
22
 
23
- **执行本 skill 前必须先 Read 同目录 `user-config.md`**:默认配置(REPO/LANG/CHANGELOG_FILE/REL_DIR/STATS/SCOPE_HINT)都在该文件中,以它为准。升级技能时 user-config.md 不会被覆盖,你的修改永久保留;官方更新配置时另存 `user-config.md.upstream` 对照文件,可按需手动合并新配置点。
23
+ **执行本 skill 前必须先 Read 同目录 `user-config.md`**:默认配置(REPO/LANG/CHANGELOG_FILE/REL_DIR/STATS/SCOPE_HINT)都在该文件中,以它为准。升级技能时 user-config.md 不会被覆盖,你的修改永久保留;官方更新配置时另存 `user-config.upstream.md` 对照文件,可按需手动合并新配置点。
24
24
 
25
25
  ## 配置版本机制(升级保护)
26
26
 
27
27
  本 skill 当前配置版本为 `1`(对应 user-config.md 中的 `CONFIG_VERSION`)。执行时若发现 user-config.md 的 CONFIG_VERSION 与本节声明不一致:
28
28
 
29
- 1. 先将旧 user-config.md 备份为 `user-config.md.bak-<时间戳>`(同目录,绝不删除旧值)
29
+ 1. 先将旧 user-config.md 备份为 `user-config.bak-<时间戳>.md`(同目录,绝不删除旧值)
30
30
  2. 按本节「配置升级规则」迁移字段:新增字段补默认值、改名字段按映射改写、删除字段的值保留在备份中并在对话提示用户手动确认
31
31
  3. 更新 CONFIG_VERSION 为当前版本
32
32
  4. 对话中明确告知用户「检测到配置版本从 X 升级到 Y,旧配置已备份至 <路径>」
@@ -361,6 +361,7 @@ footer{text-align:center;font-size:10px;color:var(--muted);letter-spacing:1px;pa
361
361
  <tr><td>查全部命令</td><td><span class="cmd-in">skill-forge --help</span></td></tr>
362
362
  <tr><td>问单个技能用法</td><td>直接问「XX 技能怎么用」,AI 会读该技能的使用说明输出</td></tr>
363
363
  <tr><td>问 SkillForge 本身</td><td>问「skillforge 怎么用」,AI 朗读本指引并打开排版页</td></tr>
364
+ <tr><td>查技能双语简介 / 实操文章</td><td><span class="cmd-in">SKILLS_CATALOG.md</span>(21 技能逐条中英简介)/ <span class="cmd-in">docs/articles/</span>(实操文章)/ <span class="cmd-in">docs/directory-submission.md</span>(目录收录素材)</td></tr>
364
365
  </table>
365
366
  <div class="faq">
366
367
  <div class="faq-title">常见问题</div>
@@ -122,6 +122,7 @@ skill-forge init --skills daily-report,weekly-report --yes
122
122
  | 查全部命令 | skill-forge --help |
123
123
  | 问单个技能用法 | 直接问「XX 技能怎么用」,AI 会读该技能的使用说明输出 |
124
124
  | 问 SkillForge 本身 | 问「skillforge 怎么用」,AI 朗读本指引并打开排版页 |
125
+ | 查技能双语简介 / 实操文章 | SKILLS_CATALOG.md(21 技能逐条中英简介)/ docs/articles/(实操文章)/ docs/directory-submission.md(目录收录素材) |
125
126
 
126
127
  ## 六、常见问题
127
128
 
@@ -1,5 +1,5 @@
1
1
  # user-config.md(本文件不会被 skill 升级覆盖,你的修改永久保留)
2
- # 官方配置更新时,另存 user-config.md.upstream 对照文件,可手动合并新配置点
2
+ # 官方配置更新时,另存 user-config.upstream.md 对照文件,可手动合并新配置点
3
3
 
4
4
  ```yaml
5
5
  CONFIG_VERSION: 1 # 配置结构版本,skill 升级迁移依据
@@ -19,13 +19,13 @@ disable-model-invocation: false
19
19
 
20
20
  ## ⚙️ 配置与自定义
21
21
 
22
- **执行本 skill 前必须先 Read 同目录 `user-config.md`**:默认配置(TARGET/CHECKS/FIX)都在该文件中,以它为准。升级技能时 user-config.md 不会被覆盖,你的修改永久保留;官方更新配置时另存 `user-config.md.upstream` 对照文件,可按需手动合并新配置点。
22
+ **执行本 skill 前必须先 Read 同目录 `user-config.md`**:默认配置(TARGET/CHECKS/FIX)都在该文件中,以它为准。升级技能时 user-config.md 不会被覆盖,你的修改永久保留;官方更新配置时另存 `user-config.upstream.md` 对照文件,可按需手动合并新配置点。
23
23
 
24
24
  ## 配置版本机制(升级保护)
25
25
 
26
26
  本 skill 当前配置版本为 `1`(对应 user-config.md 中的 `CONFIG_VERSION`)。执行时若发现 user-config.md 的 CONFIG_VERSION 与本节声明不一致:
27
27
 
28
- 1. 先将旧 user-config.md 备份为 `user-config.md.bak-<时间戳>`(同目录,绝不删除旧值)
28
+ 1. 先将旧 user-config.md 备份为 `user-config.bak-<时间戳>.md`(同目录,绝不删除旧值)
29
29
  2. 按本节「配置升级规则」迁移字段:新增字段补默认值、改名字段按映射改写、删除字段的值保留在备份中并在对话提示用户手动确认
30
30
  3. 更新 CONFIG_VERSION 为当前版本
31
31
  4. 对话中明确告知用户「检测到配置版本从 X 升级到 Y,旧配置已备份至 <路径>」
@@ -361,6 +361,7 @@ footer{text-align:center;font-size:10px;color:var(--muted);letter-spacing:1px;pa
361
361
  <tr><td>查全部命令</td><td><span class="cmd-in">skill-forge --help</span></td></tr>
362
362
  <tr><td>问单个技能用法</td><td>直接问「XX 技能怎么用」,AI 会读该技能的使用说明输出</td></tr>
363
363
  <tr><td>问 SkillForge 本身</td><td>问「skillforge 怎么用」,AI 朗读本指引并打开排版页</td></tr>
364
+ <tr><td>查技能双语简介 / 实操文章</td><td><span class="cmd-in">SKILLS_CATALOG.md</span>(21 技能逐条中英简介)/ <span class="cmd-in">docs/articles/</span>(实操文章)/ <span class="cmd-in">docs/directory-submission.md</span>(目录收录素材)</td></tr>
364
365
  </table>
365
366
  <div class="faq">
366
367
  <div class="faq-title">常见问题</div>
@@ -122,6 +122,7 @@ skill-forge init --skills daily-report,weekly-report --yes
122
122
  | 查全部命令 | skill-forge --help |
123
123
  | 问单个技能用法 | 直接问「XX 技能怎么用」,AI 会读该技能的使用说明输出 |
124
124
  | 问 SkillForge 本身 | 问「skillforge 怎么用」,AI 朗读本指引并打开排版页 |
125
+ | 查技能双语简介 / 实操文章 | SKILLS_CATALOG.md(21 技能逐条中英简介)/ docs/articles/(实操文章)/ docs/directory-submission.md(目录收录素材) |
125
126
 
126
127
  ## 六、常见问题
127
128
 
@@ -1,5 +1,5 @@
1
1
  # user-config.md(本文件不会被 skill 升级覆盖,你的修改永久保留)
2
- # 官方配置更新时,另存 user-config.md.upstream 对照文件,可手动合并新配置点
2
+ # 官方配置更新时,另存 user-config.upstream.md 对照文件,可手动合并新配置点
3
3
 
4
4
  ```yaml
5
5
  CONFIG_VERSION: 1 # 配置结构版本,skill 升级迁移依据
@@ -1,12 +1,6 @@
1
1
  ---
2
2
  name: spec-doc2spec
3
- description: >
4
- 文档转高质量 Markdown Skill(doc2spec)。把 Word / PDF / Markdown 文档转成结构化 Markdown——Pandoc 负责机械转换,AI 全程校对防错(量化校验断言、双通道完整性比对、表格列表修复、图片提取与 EMF/WMF 自动转 PNG、乱码与修订痕清理),转换后整理成干净的正文稿落盘。
5
- 可选「需求加工档」:用户要求整理成需求规格时,另出需求点+验收标准与功能单元切片。
6
- 图片自动提取到 assets/images(可配置 IMG_DIR);输出默认源文档同目录下 {文档名}-spec/ 子目录,可配置 OUTPUT_DIR(绝对路径同样按文档名建子目录,多文档互不覆盖)。
7
- 跨系统适配 macOS / Windows / Linux(Pandoc 安装、EMF 转换工具链探测)。支持 .docx(Pandoc 转换)/ .md(直接读取)/ .pdf(LibreOffice 转 docx 优先保留图片版式,降级 pdftotext 纯文本)。
8
- 不处理一般性的文档内容讨论(如「帮我看看这份文档讲了什么」),只处理「文档 → Markdown / 需求规格」的转换任务。
9
- 用户询问用法(doc2spec 怎么用/使用说明/能干什么)时走使用说明应答:读 resources/usage-guide.md 原样输出(首行标题改为「doc2spec 使用指南」),不执行任何转换命令。
3
+ description: 文档转高质量 Markdown Skill(doc2spec)。把 Word / PDF / Markdown 文档转成结构化 Markdown——Pandoc 负责机械转换,AI 全程校对防错(量化校验断言、双通道完整性比对、表格列表修复、图片提取与 EMF/WMF 自动转 PNG、乱码与修订痕清理),转换后整理成干净的正文稿落盘。 可选「需求加工档」:用户要求整理成需求规格时,另出需求点+验收标准与功能单元切片。 图片自动提取到 assets/images(可配置 IMG_DIR);输出默认源文档同目录下 {文档名}-spec/ 子目录,可配置 OUTPUT_DIR(绝对路径同样按文档名建子目录,多文档互不覆盖)。 跨系统适配 macOS / Windows / Linux(Pandoc 安装、EMF 转换工具链探测)。支持 .docx(Pandoc 转换)/ .md(直接读取)/ .pdf(LibreOffice 转 docx 优先保留图片版式,降级 pdftotext 纯文本)。 不处理一般性的文档内容讨论(如「帮我看看这份文档讲了什么」),只处理「文档 → Markdown / 需求规格」的转换任务。 用户询问用法(doc2spec 怎么用/使用说明/能干什么)时走使用说明应答:读 resources/usage-guide.md 原样输出(首行标题改为「doc2spec 使用指南」),不执行任何转换命令。
10
4
  allowed-tools: ["Bash", "Read", "Write", "Edit"]
11
5
  disable-model-invocation: false
12
6
  ---
@@ -28,13 +22,13 @@ disable-model-invocation: false
28
22
 
29
23
  ## ⚙️ 配置与自定义
30
24
 
31
- **执行本 skill 前必须先 Read 同目录 `user-config.md`**:默认配置(OUTPUT_DIR/IMG_DIR/EMF_POLICY/CONFIRM/CONFIG_VERSION)与临时口头要求说明都在该文件中,以它为准。升级技能时 user-config.md 不会被覆盖,你的修改永久保留;官方更新配置时另存 `user-config.md.upstream` 对照文件,可按需手动合并新配置点。
25
+ **执行本 skill 前必须先 Read 同目录 `user-config.md`**:默认配置(OUTPUT_DIR/IMG_DIR/EMF_POLICY/CONFIRM/CONFIG_VERSION)与临时口头要求说明都在该文件中,以它为准。升级技能时 user-config.md 不会被覆盖,你的修改永久保留;官方更新配置时另存 `user-config.upstream.md` 对照文件,可按需手动合并新配置点。
32
26
 
33
27
  ## 配置版本机制(升级保护)
34
28
 
35
29
  本 skill 当前配置版本为 `2`(对应 user-config.md 中的 `CONFIG_VERSION`)。执行时若发现 user-config.md 的 CONFIG_VERSION 与本节声明的当前版本不一致:
36
30
 
37
- 1. 先将旧 user-config.md 备份为 `user-config.md.bak-<时间戳>`(同目录,绝不删除旧值)
31
+ 1. 先将旧 user-config.md 备份为 `user-config.bak-<时间戳>.md`(同目录,绝不删除旧值)
38
32
  2. 按本节「配置升级规则」迁移字段:新增字段补默认值、改名字段按映射改写、删除字段的值保留在备份中并在对话提示用户手动确认
39
33
  3. 更新 CONFIG_VERSION 为当前版本
40
34
  4. 对话中明确告知用户「检测到配置版本从 X 升级到 Y,旧配置已备份至 <路径>」