dsh-mattpocock-skills-deck 1.7.13 → 1.7.14
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 +2 -2
- package/lib/bootstrap.js +207 -0
- package/lib/client.js +3876 -3584
- package/lib/commentThreads.js +242 -0
- package/lib/detectChain.js +249 -0
- package/lib/handoffClaim.js +161 -0
- package/lib/index.js +323 -3993
- package/lib/issueDetail.js +304 -0
- package/lib/issueList.js +279 -0
- package/lib/mapBody.js +81 -0
- package/lib/namingGuardian.js +348 -0
- package/lib/pickerShell.js +143 -0
- package/lib/platformChannel.js +242 -0
- package/lib/publishFlow.js +181 -0
- package/lib/remotePredicates.js +74 -0
- package/lib/repoKeys.js +234 -0
- package/lib/sessionLifecycle.js +56 -0
- package/lib/sessionRefresh.js +314 -0
- package/lib/sessionSnapshot.js +323 -0
- package/lib/skillProbe.js +333 -0
- package/lib/ticketGrouping.js +62 -0
- package/lib/tracker/backends/README.md +33 -33
- package/lib/tracker/backends/github/backend.js +106 -0
- package/lib/tracker/backends/github/checks.js +55 -0
- package/lib/tracker/backends/github/index.js +17 -484
- package/lib/tracker/backends/github/init-project.js +213 -0
- package/lib/tracker/backends/github/issues-write.js +276 -0
- package/lib/tracker/backends/github/issues.js +9 -270
- package/lib/tracker/backends/github/repo.js +139 -0
- package/lib/tracker/backends/gitlab/graph-blocking.js +245 -0
- package/lib/tracker/backends/gitlab/graph-membership.js +101 -0
- package/lib/tracker/backends/gitlab/graph.js +7 -344
- package/lib/tracker/backends/markdown/issues-create.js +86 -0
- package/lib/tracker/backends/markdown/issues-labels.js +45 -0
- package/lib/tracker/backends/markdown/issues-locate.js +77 -0
- package/lib/tracker/backends/markdown/issues-patch.js +185 -0
- package/lib/tracker/backends/markdown/issues-read.js +192 -0
- package/lib/tracker/backends/markdown/issues-status.js +140 -0
- package/lib/tracker/backends/markdown/issues.js +11 -683
- package/lib/tracker/fixContract.js +2 -2
- package/lib/tracker/generic.js +11 -8
- package/lib/tracker/index.js +3 -1
- package/lib/tracker/predicateCore.js +120 -0
- package/lib/tracker/{predicateRegistry.js → predicatePrimitives.js} +9 -120
- package/lib/tracker/{registry.js → registryCore.js} +199 -348
- package/lib/tracker/registryShape.js +81 -0
- package/lib/tracker/registryViews.js +85 -0
- package/lib/workspaceCwd.js +93 -0
- package/package.json +2 -2
- package/shared/naming-attribution.js +172 -0
- package/shared/naming-titles.js +146 -0
- package/shared/{naming-guardian.js → naming-tracking.js} +285 -498
- package/shared/tracker/chain-evaluate.js +268 -0
- package/shared/tracker/chain-types.js +233 -0
- package/shared/tracker/chain-validate.js +244 -0
- package/shared/tracker/{check-catalog.js → check-catalog-dirs.js} +245 -356
- package/shared/tracker/check-catalog-views.js +154 -0
- package/shared/tracker/chain.js +0 -682
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
> 唯一打开形式)+ GitHub issue 动作注入(诊断 / 修复 / 讨论 / 执行,均带 `/wayfinder` `/triage` 技能命令)
|
|
8
8
|
> + 交接开新会话。
|
|
9
9
|
|
|
10
|
-
- **插件包名**: `dsh-mattpocock-skills-deck`(可分发 npm 包,见 `package/`,当前 **v1.7.
|
|
10
|
+
- **插件包名**: `dsh-mattpocock-skills-deck`(可分发 npm 包,见 `package/`,当前 **v1.7.14**(`latest`,品牌迁移自 dsh-waystation v1.5.0,`npm view` 已验):v1.4 详情页(#441)+ v1.5 升级 map 全量落地 —— 状态栏 BUG/诊断过滤 + 设置直达 + 删 panelHeight + Matt 引导 + 新增 wayfinder + 仓库身份 + 双语 + 仓库级缓存架构(git 根检测 → 磁盘缓存秒开 → 自动探测刷新)+ 进度契约与阶段闸门 + 正文格式容错 + markdown 白名单渲染 + 3 项 bug 修复 + T10 自动刷新机制(变化行高亮/即时转圈反馈))
|
|
11
11
|
- **动态版 pluginId**: `wfst-1`(v9–v24 迭代产物)
|
|
12
12
|
- **平台**: Client(浏览器页面)+ Host(Node 进程,gh CLI 数据层)
|
|
13
13
|
- **配套**: [mattpocock/skills](https://github.com/mattpocock/skills)(wayfinder / triage / grilling / handoff / ask-matt 等)
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
| 进度显示 | map 行右侧 18px 迷你圆环 + n/total(等宽数字右缘对齐,圆环零间隙) |
|
|
23
23
|
| 面板 · 技能 | 技能雷达(推荐 / 列表 / 圆环),点击注入 /skill |
|
|
24
24
|
| 面板 · 环境检查 | 8 项前置检查(仓库定位 / setup / tracker / gh CLI / 登录 / API / 技能探测),红黄绿分组 + 一键处理 |
|
|
25
|
-
| tabs 行 | 列表 / 技能 / 环境检查 + 右侧刷新按钮 + 最右侧版本号(如 v1.7.
|
|
25
|
+
| tabs 行 | 列表 / 技能 / 环境检查 + 右侧刷新按钮 + 最右侧版本号(如 v1.7.14),便于核对已更新 |
|
|
26
26
|
| 行级动作 | 按 label 四选一:诊断(/triage) / 修复(/wayfinder) / 讨论(/wayfinder) / 执行(/wayfinder),按钮色 = GitHub label 配置色,点击预填输入框;执行/新会话按钮常显(复制/外链也常显) |
|
|
27
27
|
| map 详情 | 顶部「执行」+ 任务按状态动作、可接/已认领/被阻塞/已关闭垂直走廊、Decision/Fog/Out-of-scope 折叠 |
|
|
28
28
|
| 交接 | 第一击注入 /handoff 时间戳模板;第二击预填 /read + 复述确认 prompt 并开新会话 |
|
package/lib/bootstrap.js
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
// src/host/bootstrap.js —— 启动技底与技能名单(H1 #445 从 host/index.js 31–215/235–249 搬出,纯结构、行为零变化)
|
|
2
|
+
// 以后谁改它:改启动装配、兜底技能 provider 或技能名单惰性加载的人。预估约 250 行,超 350 打回。
|
|
3
|
+
// 接线:由 index.js 动态 import 动态加载,依赖全显式传入(仅 ctx);本文件不引用其他新文件。
|
|
4
|
+
export function createBootstrap(deps) {
|
|
5
|
+
const { ctx } = deps
|
|
6
|
+
// === T2 #389 bundled 兜底 provider(rank 600,trustedHost,ctx.effect 托管)===
|
|
7
|
+
// 零代码声明:无需 env 晚置,直接 registerProvider;list 返回 package/bundled-skills 的 25 个,rank 600 兜底,bundled 随包消失
|
|
8
|
+
// 选择 provider 而非 env 的依据见 R1 研究(env 构造时一次性读,晚置失效);参见 docs/adr/20260828-skill-probe-union-channels.md 的 trustedHost 约束
|
|
9
|
+
;(() => {
|
|
10
|
+
try {
|
|
11
|
+
const skills = ctx.get('skills')
|
|
12
|
+
if (!skills || typeof skills.registerProvider !== 'function') return
|
|
13
|
+
;(async () => {
|
|
14
|
+
let bundledDir = null
|
|
15
|
+
try {
|
|
16
|
+
const pathMod = await import('node:path')
|
|
17
|
+
const fsSync = await import('node:fs')
|
|
18
|
+
const osMod = await import('node:os')
|
|
19
|
+
const candidates = []
|
|
20
|
+
const cwd = (typeof process !== 'undefined' && typeof process.cwd === 'function') ? String(process.cwd() || '') : ''
|
|
21
|
+
if (cwd) {
|
|
22
|
+
candidates.push(pathMod.resolve(cwd, 'package/bundled-skills'))
|
|
23
|
+
candidates.push(pathMod.resolve(cwd, '../package/bundled-skills'))
|
|
24
|
+
candidates.push(pathMod.resolve(cwd, '../../package/bundled-skills'))
|
|
25
|
+
candidates.push(pathMod.resolve(cwd, 'bundled-skills'))
|
|
26
|
+
let cur = cwd
|
|
27
|
+
for (let i = 0; i < 4; i++) {
|
|
28
|
+
candidates.push(pathMod.join(cur, 'package/bundled-skills'))
|
|
29
|
+
candidates.push(pathMod.join(cur, 'node_modules/dsh-mattpocock-skills-deck/bundled-skills'))
|
|
30
|
+
cur = pathMod.dirname(cur)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
const home = osMod.homedir()
|
|
35
|
+
if (home) {
|
|
36
|
+
candidates.push(pathMod.join(home, '.dsh/profiles/web/node_modules/dsh-mattpocock-skills-deck/bundled-skills'))
|
|
37
|
+
candidates.push(pathMod.join(home, '.dsh/profiles/desktop/node_modules/dsh-mattpocock-skills-deck/bundled-skills'))
|
|
38
|
+
try {
|
|
39
|
+
const profilesDir = pathMod.join(home, '.dsh/profiles')
|
|
40
|
+
const entries = await (await import('node:fs/promises')).readdir(profilesDir, { withFileTypes: true }).catch(() => [])
|
|
41
|
+
for (const e of entries) if (e.isDirectory()) candidates.push(pathMod.join(profilesDir, e.name, 'node_modules/dsh-mattpocock-skills-deck/bundled-skills'))
|
|
42
|
+
} catch {}
|
|
43
|
+
}
|
|
44
|
+
} catch {}
|
|
45
|
+
const seen = new Set()
|
|
46
|
+
const uniq = []
|
|
47
|
+
for (const c of candidates) {
|
|
48
|
+
const n = pathMod.normalize(c)
|
|
49
|
+
if (!seen.has(n)) { seen.add(n); uniq.push(n) }
|
|
50
|
+
}
|
|
51
|
+
for (const cand of uniq) {
|
|
52
|
+
try {
|
|
53
|
+
const st = fsSync.statSync(cand)
|
|
54
|
+
if (st && st.isDirectory()) {
|
|
55
|
+
try {
|
|
56
|
+
const way = pathMod.join(cand, 'wayfinder', 'SKILL.md')
|
|
57
|
+
const wst = fsSync.statSync(way)
|
|
58
|
+
if (wst && wst.isFile()) { bundledDir = cand; break }
|
|
59
|
+
} catch {}
|
|
60
|
+
try {
|
|
61
|
+
const ents = fsSync.readdirSync(cand, { withFileTypes: true })
|
|
62
|
+
const dcount = ents.filter(e => e.isDirectory()).length
|
|
63
|
+
if (dcount >= 20) { bundledDir = cand; break }
|
|
64
|
+
} catch {}
|
|
65
|
+
}
|
|
66
|
+
} catch {}
|
|
67
|
+
}
|
|
68
|
+
} catch (e) { bundledDir = null }
|
|
69
|
+
if (!bundledDir) {
|
|
70
|
+
try { ctx.get('logger')?.warn?.('[bundled] dir not found, skip provider') } catch {}
|
|
71
|
+
return
|
|
72
|
+
}
|
|
73
|
+
function parseSkillRaw(raw) {
|
|
74
|
+
try {
|
|
75
|
+
const s = String(raw || '').replace(/^/, '')
|
|
76
|
+
const m = s.match(/^---\s*\r?\n([\s\S]*?)\r?\n---/)
|
|
77
|
+
if (!m) return undefined
|
|
78
|
+
const front = m[1]
|
|
79
|
+
const body = s.slice(m[0].length)
|
|
80
|
+
const getField = (key) => {
|
|
81
|
+
const re = new RegExp('^\\s*' + key.replace(/-/g, '\\-') + '\\s*:\\s*(.+)$', 'm')
|
|
82
|
+
const mm = front.match(re)
|
|
83
|
+
if (!mm) return undefined
|
|
84
|
+
let v = mm[1].trim()
|
|
85
|
+
if ((v.startsWith('"') && v.endsWith('"')) || (v.startsWith("'") && v.endsWith("'"))) v = v.slice(1, -1)
|
|
86
|
+
return v.trim()
|
|
87
|
+
}
|
|
88
|
+
const name = getField('name')
|
|
89
|
+
const description = getField('description')
|
|
90
|
+
if (!name || !description) return undefined
|
|
91
|
+
const whenToUse = getField('whenToUse')
|
|
92
|
+
const parseBool = (val) => {
|
|
93
|
+
if (val === undefined) return undefined
|
|
94
|
+
const l = String(val).toLowerCase().trim()
|
|
95
|
+
if (l === 'true' || l === 'yes' || l === 'on' || l === '1') return true
|
|
96
|
+
if (l === 'false' || l === 'no' || l === 'off' || l === '0') return false
|
|
97
|
+
return undefined
|
|
98
|
+
}
|
|
99
|
+
const disableModel = parseBool(getField('disable-model-invocation'))
|
|
100
|
+
const userInv = parseBool(getField('user-invocable'))
|
|
101
|
+
const invocation = { modelInvocable: disableModel !== true, userInvocable: userInv !== false }
|
|
102
|
+
return { name, description, whenToUse: whenToUse || undefined, invocation, body: body.trim(), content: body.trim() }
|
|
103
|
+
} catch { return undefined }
|
|
104
|
+
}
|
|
105
|
+
function isValidSkillName(n) { try { return /^[\p{L}0-9]+(?:-[\p{L}0-9]+)*$/u.test(n) } catch { return /^[a-z0-9]+(?:-[a-z0-9]+)*$/i.test(n) } }
|
|
106
|
+
const BUNDLED_RANK = 600
|
|
107
|
+
const PROVIDER_NAME = 'bundled-mattpocock'
|
|
108
|
+
const createBundledProvider = (control) => {
|
|
109
|
+
return {
|
|
110
|
+
name: PROVIDER_NAME,
|
|
111
|
+
async list(opts) {
|
|
112
|
+
try {
|
|
113
|
+
const fsp = await import('node:fs/promises')
|
|
114
|
+
const pathMod2 = await import('node:path')
|
|
115
|
+
if (opts && opts.signal && opts.signal.aborted) throw opts.signal.reason
|
|
116
|
+
let entries = []
|
|
117
|
+
try { entries = await fsp.readdir(bundledDir, { withFileTypes: true }) } catch { return [] }
|
|
118
|
+
const out = []
|
|
119
|
+
for (const ent of entries) {
|
|
120
|
+
if (!ent.isDirectory()) continue
|
|
121
|
+
const dirName = ent.name
|
|
122
|
+
if (!isValidSkillName(dirName)) continue
|
|
123
|
+
const mdPath = pathMod2.join(bundledDir, dirName, 'SKILL.md')
|
|
124
|
+
try {
|
|
125
|
+
const raw = await fsp.readFile(mdPath, 'utf8')
|
|
126
|
+
const parsed = parseSkillRaw(raw)
|
|
127
|
+
if (!parsed) continue
|
|
128
|
+
if (parsed.name !== dirName) continue
|
|
129
|
+
out.push({
|
|
130
|
+
name: parsed.name,
|
|
131
|
+
description: parsed.description,
|
|
132
|
+
...(parsed.whenToUse ? { whenToUse: parsed.whenToUse } : {}),
|
|
133
|
+
invocation: parsed.invocation,
|
|
134
|
+
source: 'bundled',
|
|
135
|
+
provider: PROVIDER_NAME,
|
|
136
|
+
rank: BUNDLED_RANK,
|
|
137
|
+
locator: { path: mdPath, directory: pathMod2.join(bundledDir, dirName) },
|
|
138
|
+
resourceBase: { kind: 'directory', path: pathMod2.join(bundledDir, dirName) },
|
|
139
|
+
path: mdPath
|
|
140
|
+
})
|
|
141
|
+
} catch {}
|
|
142
|
+
}
|
|
143
|
+
out.sort((a, b) => a.name.localeCompare(b.name))
|
|
144
|
+
return out
|
|
145
|
+
} catch (e) {
|
|
146
|
+
try { ctx.get('logger')?.warn?.('[bundled] list failed: ' + String(e && e.message || e)) } catch {}
|
|
147
|
+
return []
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
async get(candidate, opts) {
|
|
151
|
+
try {
|
|
152
|
+
const fsp2 = await import('node:fs/promises')
|
|
153
|
+
if (opts && opts.signal && opts.signal.aborted) throw opts.signal.reason
|
|
154
|
+
const loc = candidate && candidate.locator ? candidate.locator : null
|
|
155
|
+
const p = (loc && loc.path) ? loc.path : (candidate && candidate.path) ? candidate.path : null
|
|
156
|
+
if (!p) return undefined
|
|
157
|
+
let raw
|
|
158
|
+
try { raw = await fsp2.readFile(p, 'utf8') } catch { return undefined }
|
|
159
|
+
const parsed = parseSkillRaw(raw)
|
|
160
|
+
if (!parsed) return undefined
|
|
161
|
+
if (parsed.name !== candidate.name) return undefined
|
|
162
|
+
const dir = loc && loc.directory ? loc.directory : (await import('node:path')).dirname(p)
|
|
163
|
+
if (opts && opts.signal && opts.signal.aborted) throw opts.signal.reason
|
|
164
|
+
return {
|
|
165
|
+
name: parsed.name,
|
|
166
|
+
description: parsed.description,
|
|
167
|
+
...(parsed.whenToUse ? { whenToUse: parsed.whenToUse } : {}),
|
|
168
|
+
invocation: parsed.invocation,
|
|
169
|
+
source: 'bundled',
|
|
170
|
+
provider: PROVIDER_NAME,
|
|
171
|
+
resourceBase: { kind: 'directory', path: dir },
|
|
172
|
+
path: p,
|
|
173
|
+
content: parsed.content
|
|
174
|
+
}
|
|
175
|
+
} catch { return undefined }
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
let dispose = null
|
|
180
|
+
try {
|
|
181
|
+
dispose = skills.registerProvider(createBundledProvider)
|
|
182
|
+
try { ctx.effect(() => () => { try { dispose && dispose() } catch {} }) } catch {}
|
|
183
|
+
try { ctx.get('logger')?.info?.('[bundled] provider registered at ' + bundledDir + ' rank ' + BUNDLED_RANK) } catch {}
|
|
184
|
+
console.log('[bundled] provider registered at ' + bundledDir + ' rank ' + BUNDLED_RANK + ' (25 skills expected)')
|
|
185
|
+
} catch (e) {
|
|
186
|
+
try { console.warn('[bundled] registerProvider failed ' + String(e && e.message || e)) } catch {}
|
|
187
|
+
}
|
|
188
|
+
})()
|
|
189
|
+
} catch {}
|
|
190
|
+
})()
|
|
191
|
+
// 技能名单(#280 单一真源:与 check-catalog + client SKILLS 同步;拼写以真实目录为准,B 语义由 skills.get 覆盖)
|
|
192
|
+
// 真源 = shared/matt-skills.js(MATT_SKILL_PROBE_NAMES)。本字段由 getMattSkillProbeNames() 惰性加载。
|
|
193
|
+
let SKILL_PROBE_NAMES = null
|
|
194
|
+
async function getMattSkillProbeNames() {
|
|
195
|
+
if (SKILL_PROBE_NAMES) return SKILL_PROBE_NAMES
|
|
196
|
+
try {
|
|
197
|
+
const m = await import('../shared/matt-skills.js')
|
|
198
|
+
SKILL_PROBE_NAMES = (m && (m.MATT_SKILL_PROBE_NAMES || m.default?.MATT_SKILL_PROBE_NAMES)) || null
|
|
199
|
+
if (!SKILL_PROBE_NAMES) throw new Error('shared/matt-skills.js 未导出 MATT_SKILL_PROBE_NAMES')
|
|
200
|
+
} catch (e) {
|
|
201
|
+
// 兜底:内联一份与真源一致的常量(仅在 shared 文件丢失时使用;CI/构建必须保证真源在场)
|
|
202
|
+
SKILL_PROBE_NAMES = ['ask-matt','code-review','codebase-design','diagnosing-bugs','domain-modeling','grill-with-docs','implement','improve-codebase-architecture','prototype','research','resolving-merge-conflicts','setup-matt-pocock-skills','tdd','to-spec','to-tickets','triage','wayfinder','wizard','grill-me','grilling','handoff','teach','to-questionnaire','wait-what','writing-for-agents']
|
|
203
|
+
}
|
|
204
|
+
return SKILL_PROBE_NAMES
|
|
205
|
+
}
|
|
206
|
+
return { getMattSkillProbeNames }
|
|
207
|
+
}
|