sillyspec 3.26.10 → 3.26.11
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/.claude/skills/sillyspec-archive/SKILL.md +85 -85
- package/.claude/skills/sillyspec-brainstorm/SKILL.md +114 -114
- package/.claude/skills/sillyspec-commit/SKILL.md +109 -109
- package/.claude/skills/sillyspec-continue/SKILL.md +45 -45
- package/.claude/skills/sillyspec-doctor/SKILL.md +76 -76
- package/.claude/skills/sillyspec-execute/SKILL.md +200 -200
- package/.claude/skills/sillyspec-explore/SKILL.md +95 -95
- package/.claude/skills/sillyspec-knowledge/SKILL.md +269 -269
- package/.claude/skills/sillyspec-plan/SKILL.md +148 -148
- package/.claude/skills/sillyspec-quick/SKILL.md +111 -111
- package/.claude/skills/sillyspec-resume/SKILL.md +68 -68
- package/.claude/skills/sillyspec-scan/SKILL.md +79 -79
- package/.claude/skills/sillyspec-state/SKILL.md +64 -64
- package/.claude/skills/sillyspec-status/SKILL.md +54 -54
- package/.claude/skills/sillyspec-verify/SKILL.md +83 -83
- package/.claude/skills/sillyspec-workspace/SKILL.md +169 -169
- package/.gitattributes +2 -0
- package/.husky/pre-push +20 -20
- package/.npmrc-publish +2 -0
- package/README.md +201 -201
- package/SKILL.md +90 -90
- package/package.json +39 -39
- package/packages/dashboard/dist/assets/index-Bq_Z2hne.js +7446 -7446
- package/packages/dashboard/dist/assets/index-O2W5RV4z.css +1 -1
- package/packages/dashboard/dist/index.html +16 -16
- package/packages/dashboard/package.json +25 -25
- package/packages/dashboard/server/executor.js +86 -86
- package/packages/dashboard/server/index.js +601 -601
- package/packages/dashboard/server/parser.js +526 -526
- package/src/change-risk-profile.js +254 -254
- package/src/classify-change.js +109 -109
- package/src/config-schema.js +310 -310
- package/src/contract-matrix.js +379 -379
- package/src/db.js +350 -350
- package/src/dispatch/backends/local-agent.js +155 -155
- package/src/dispatch/backends/sillyhub-mcp.js +245 -245
- package/src/dispatch/probe.js +210 -210
- package/src/dispatch/strategy.js +121 -121
- package/src/docs-check.js +131 -4
- package/src/doctor-diagnostics.js +711 -711
- package/src/endpoint-extractor.js +401 -401
- package/src/fs-atomic.js +69 -69
- package/src/git-helper.js +79 -79
- package/src/hooks/claude-pre-tool-use.cjs +125 -125
- package/src/hooks/worktree-guard.js +848 -848
- package/src/index.js +90 -8
- package/src/init.js +623 -623
- package/src/knowledge-match.js +130 -130
- package/src/local-detect.js +120 -120
- package/src/machine-interface.js +543 -543
- package/src/migrate.js +127 -127
- package/src/progress/change-registry.js +297 -260
- package/src/progress/consistency-doctor.js +355 -355
- package/src/progress/shared.js +36 -36
- package/src/progress/stage-machine.js +29 -1
- package/src/progress/step-store.js +261 -261
- package/src/progress.js +1131 -1127
- package/src/quicklog.js +693 -693
- package/src/review-tier.js +77 -77
- package/src/run/command.js +1385 -1364
- package/src/run/complete-handlers.js +1443 -1399
- package/src/run/complete.js +225 -24
- package/src/run/gates.js +755 -755
- package/src/run/prompt.js +761 -761
- package/src/run/quick-audit.js +155 -146
- package/src/run/shared.js +1268 -1215
- package/src/run/stage.js +487 -479
- package/src/scan-postcheck.js +371 -371
- package/src/setup.js +416 -416
- package/src/sillyhub-mcp/client.js +529 -529
- package/src/spec-sync.js +323 -319
- package/src/stage-contract.js +897 -897
- package/src/stage-review.js +549 -549
- package/src/stages/archive.js +155 -155
- package/src/stages/brainstorm-auto.js +236 -236
- package/src/stages/brainstorm.js +596 -597
- package/src/stages/cmd-existence.js +145 -145
- package/src/stages/doctor.js +371 -371
- package/src/stages/execute.js +1003 -1003
- package/src/stages/explore.js +34 -34
- package/src/stages/index.js +29 -29
- package/src/stages/knowledge.js +498 -498
- package/src/stages/plan-postcheck.js +1302 -1302
- package/src/stages/plan.js +625 -623
- package/src/stages/quick.js +151 -151
- package/src/stages/scan.js +607 -607
- package/src/stages/status.js +64 -64
- package/src/stages/verify.js +264 -264
- package/src/sync.js +1223 -1135
- package/src/task-review.js +1019 -1019
- package/src/verify-postcheck.js +1186 -1186
- package/src/workflow.js +600 -600
- package/src/worktree-apply.js +1213 -1136
- package/src/worktree-deps.js +353 -353
- package/src/worktree.js +1481 -1481
- package/templates/workflows/archive-impact.yaml +79 -79
- package/templates/workflows/scan-docs.yaml +132 -132
|
@@ -1,145 +1,145 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* cmd-existence.js — 共享命令存在性校验 helper(H2)
|
|
3
|
-
*
|
|
4
|
-
* 抽自 scan-postcheck.js:118-158 的 `npm run <script>` 存在性判定,扩展到 pnpm/yarn,
|
|
5
|
-
* 并感知 monorepo 子目录(`cd <subdir> &&` 前缀 或 local.yaml `modules` 块定位)。
|
|
6
|
-
*
|
|
7
|
-
* 严重度由调用方决定(D-04 同 helper 双严重度):
|
|
8
|
-
* - scan-postcheck 调用时维持 warning(local.yaml 命令误报不阻断 init)
|
|
9
|
-
* - plan-postcheck 调用时升 error(plan 阶段命令更结构化可硬阻断)
|
|
10
|
-
*
|
|
11
|
-
* 仅校验 `npm|pnpm|yarn run <script>` 这一类可静态对账的命令;
|
|
12
|
-
* install / typecheck / npx / uv run 等直接包管理器调用不在范围(沿用 scan-postcheck:126-129 注释立场)。
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
import { existsSync, readFileSync } from 'fs'
|
|
16
|
-
import { join } from 'path'
|
|
17
|
-
|
|
18
|
-
// 提取 `npm|pnpm|yarn run <script>` 命令。
|
|
19
|
-
// 对齐 scan-postcheck:136 的 `/npm run (\S+)/g`,扩展包管理器 + 容忍多空白。
|
|
20
|
-
// 捕获组 1 = 包管理器(npm|pnpm|yarn),捕获组 2 = script 名。
|
|
21
|
-
const SCRIPT_CMD_RE = /(npm|pnpm|yarn)\s+run\s+(\S+)/g
|
|
22
|
-
|
|
23
|
-
// 识别紧贴命令前的 `cd <subdir> &&` 前缀(anchored at end of the slice before the command)。
|
|
24
|
-
// subdir token 不含空白与 & ; | 等分隔符;末尾的 `&&` 可带任意空白后接命令。
|
|
25
|
-
const CD_PREFIX_RE = /cd\s+([^\s&;|()]+)\s*&&\s*$/
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* 读取 package.json 的 scripts 字段。
|
|
29
|
-
* @param {string} pkgPath
|
|
30
|
-
* @returns {{ status: 'ok'|'missing'|'unreadable'|'no-scripts', scripts: object|null }}
|
|
31
|
-
* - ok : 文件存在且 JSON 解析成功,含 scripts 对象
|
|
32
|
-
* - missing : 文件不存在
|
|
33
|
-
* - unreadable : 文件存在但 JSON 解析失败(与 scan-postcheck:147 try/catch 一致,不抛)
|
|
34
|
-
* - no-scripts : 解析成功但无 scripts 字段(视作 script 不存在)
|
|
35
|
-
*/
|
|
36
|
-
function readScripts(pkgPath) {
|
|
37
|
-
if (!existsSync(pkgPath)) return { status: 'missing', scripts: null }
|
|
38
|
-
try {
|
|
39
|
-
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'))
|
|
40
|
-
if (pkg && typeof pkg === 'object' && pkg.scripts && typeof pkg.scripts === 'object') {
|
|
41
|
-
return { status: 'ok', scripts: pkg.scripts }
|
|
42
|
-
}
|
|
43
|
-
return { status: 'no-scripts', scripts: null }
|
|
44
|
-
} catch {
|
|
45
|
-
return { status: 'unreadable', scripts: null }
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* 为单条命令计算候选 package.json 路径列表。
|
|
51
|
-
*
|
|
52
|
-
* 规则(blueprint task-02 + design §5 H2):
|
|
53
|
-
* - 有 `cd <subdir> &&` 前缀 → 仅查 `<projectRoot>/<subdir>/package.json`(cd 锁定子目录,不回退根)
|
|
54
|
-
* - 无前缀 + modules 块提供 → 查每个 module.path 的子包 + 根 package.json(多候选任一命中即视为存在)
|
|
55
|
-
* - 无前缀 + 无 modules → 仅查 `<projectRoot>/package.json`
|
|
56
|
-
*
|
|
57
|
-
* @param {string} text 全文
|
|
58
|
-
* @param {number} matchIndex 当前命令在 text 中的起始位置
|
|
59
|
-
* @param {string} projectRoot
|
|
60
|
-
* @param {object|null|undefined} modules local.yaml modules 块({ name: { path, test } })
|
|
61
|
-
* @returns {string[]} 候选 package.json 绝对路径列表(至少 1 个)
|
|
62
|
-
*/
|
|
63
|
-
function resolveCandidates(text, matchIndex, projectRoot, modules) {
|
|
64
|
-
const before = text.slice(0, matchIndex)
|
|
65
|
-
const cdMatch = before.match(CD_PREFIX_RE)
|
|
66
|
-
if (cdMatch) {
|
|
67
|
-
const subdir = cdMatch[1].replace(/^["']|["']$/g, '')
|
|
68
|
-
return [join(projectRoot, subdir, 'package.json')]
|
|
69
|
-
}
|
|
70
|
-
if (modules && typeof modules === 'object') {
|
|
71
|
-
const candidates = []
|
|
72
|
-
for (const [, mod] of Object.entries(modules)) {
|
|
73
|
-
if (mod && typeof mod === 'object' && typeof mod.path === 'string' && mod.path.trim() !== '') {
|
|
74
|
-
candidates.push(join(projectRoot, mod.path, 'package.json'))
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
candidates.push(join(projectRoot, 'package.json'))
|
|
78
|
-
return candidates
|
|
79
|
-
}
|
|
80
|
-
return [join(projectRoot, 'package.json')]
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* 校验文本中所有 `npm|pnpm|yarn run <script>` 命令在 package.json scripts 中存在(monorepo 感知)。
|
|
85
|
-
*
|
|
86
|
-
* @param {string} text 待校验文本(local.yaml 内容 / TaskCard verify+implementation 字段等)
|
|
87
|
-
* @param {{ projectRoot: string, modules?: object|null }} opts
|
|
88
|
-
* - projectRoot : 项目根目录绝对路径
|
|
89
|
-
* - modules : local.yaml modules 块(可选),结构如 `{ frontend: { path: "frontend/", test: "..." } }`
|
|
90
|
-
* @returns {{ invalid: Array<{ cmd: string, reason: string }>, checked: number }}
|
|
91
|
-
* - invalid : 找不到 script 的命令列表,每项含 { cmd, reason };reason 区分
|
|
92
|
-
* 「package.json 不存在」「package.json 解析失败」「package.json 无 <script> script」
|
|
93
|
-
* - checked : 从 text 提取到的命令总数(含合法与非法)
|
|
94
|
-
*/
|
|
95
|
-
export function validateScriptCommands(text, { projectRoot, modules } = {}) {
|
|
96
|
-
const invalid = []
|
|
97
|
-
if (!text || typeof text !== 'string') {
|
|
98
|
-
return { invalid, checked: 0 }
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const matches = [...text.matchAll(SCRIPT_CMD_RE)]
|
|
102
|
-
const checked = matches.length
|
|
103
|
-
|
|
104
|
-
for (const m of matches) {
|
|
105
|
-
const pm = m[1]
|
|
106
|
-
const scriptName = m[2]
|
|
107
|
-
const cmd = `${pm} run ${scriptName}`
|
|
108
|
-
|
|
109
|
-
const candidates = resolveCandidates(text, m.index ?? 0, projectRoot, modules)
|
|
110
|
-
|
|
111
|
-
let found = false
|
|
112
|
-
let existedAny = false // 至少一个候选文件存在且可解析(ok 或 no-scripts)
|
|
113
|
-
let parseFailedAny = false // 至少一个候选文件解析失败(unreadable)
|
|
114
|
-
for (const pkgPath of candidates) {
|
|
115
|
-
const result = readScripts(pkgPath)
|
|
116
|
-
if (result.status === 'ok') {
|
|
117
|
-
existedAny = true
|
|
118
|
-
if (Object.prototype.hasOwnProperty.call(result.scripts, scriptName)) {
|
|
119
|
-
found = true
|
|
120
|
-
break
|
|
121
|
-
}
|
|
122
|
-
} else if (result.status === 'no-scripts') {
|
|
123
|
-
existedAny = true
|
|
124
|
-
// scripts 字段缺失 → 该候选肯定不含此 script,继续看其他候选
|
|
125
|
-
} else if (result.status === 'unreadable') {
|
|
126
|
-
parseFailedAny = true
|
|
127
|
-
}
|
|
128
|
-
// missing → 既不算 existed 也不算 parseFailed
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
if (!found) {
|
|
132
|
-
let reason
|
|
133
|
-
if (!existedAny && parseFailedAny) {
|
|
134
|
-
reason = `package.json 解析失败: ${candidates.join(', ')}`
|
|
135
|
-
} else if (!existedAny) {
|
|
136
|
-
reason = `package.json 不存在: ${candidates.join(', ')}`
|
|
137
|
-
} else {
|
|
138
|
-
reason = `package.json 无 ${scriptName} script`
|
|
139
|
-
}
|
|
140
|
-
invalid.push({ cmd, reason })
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
return { invalid, checked }
|
|
145
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* cmd-existence.js — 共享命令存在性校验 helper(H2)
|
|
3
|
+
*
|
|
4
|
+
* 抽自 scan-postcheck.js:118-158 的 `npm run <script>` 存在性判定,扩展到 pnpm/yarn,
|
|
5
|
+
* 并感知 monorepo 子目录(`cd <subdir> &&` 前缀 或 local.yaml `modules` 块定位)。
|
|
6
|
+
*
|
|
7
|
+
* 严重度由调用方决定(D-04 同 helper 双严重度):
|
|
8
|
+
* - scan-postcheck 调用时维持 warning(local.yaml 命令误报不阻断 init)
|
|
9
|
+
* - plan-postcheck 调用时升 error(plan 阶段命令更结构化可硬阻断)
|
|
10
|
+
*
|
|
11
|
+
* 仅校验 `npm|pnpm|yarn run <script>` 这一类可静态对账的命令;
|
|
12
|
+
* install / typecheck / npx / uv run 等直接包管理器调用不在范围(沿用 scan-postcheck:126-129 注释立场)。
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { existsSync, readFileSync } from 'fs'
|
|
16
|
+
import { join } from 'path'
|
|
17
|
+
|
|
18
|
+
// 提取 `npm|pnpm|yarn run <script>` 命令。
|
|
19
|
+
// 对齐 scan-postcheck:136 的 `/npm run (\S+)/g`,扩展包管理器 + 容忍多空白。
|
|
20
|
+
// 捕获组 1 = 包管理器(npm|pnpm|yarn),捕获组 2 = script 名。
|
|
21
|
+
const SCRIPT_CMD_RE = /(npm|pnpm|yarn)\s+run\s+(\S+)/g
|
|
22
|
+
|
|
23
|
+
// 识别紧贴命令前的 `cd <subdir> &&` 前缀(anchored at end of the slice before the command)。
|
|
24
|
+
// subdir token 不含空白与 & ; | 等分隔符;末尾的 `&&` 可带任意空白后接命令。
|
|
25
|
+
const CD_PREFIX_RE = /cd\s+([^\s&;|()]+)\s*&&\s*$/
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 读取 package.json 的 scripts 字段。
|
|
29
|
+
* @param {string} pkgPath
|
|
30
|
+
* @returns {{ status: 'ok'|'missing'|'unreadable'|'no-scripts', scripts: object|null }}
|
|
31
|
+
* - ok : 文件存在且 JSON 解析成功,含 scripts 对象
|
|
32
|
+
* - missing : 文件不存在
|
|
33
|
+
* - unreadable : 文件存在但 JSON 解析失败(与 scan-postcheck:147 try/catch 一致,不抛)
|
|
34
|
+
* - no-scripts : 解析成功但无 scripts 字段(视作 script 不存在)
|
|
35
|
+
*/
|
|
36
|
+
function readScripts(pkgPath) {
|
|
37
|
+
if (!existsSync(pkgPath)) return { status: 'missing', scripts: null }
|
|
38
|
+
try {
|
|
39
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'))
|
|
40
|
+
if (pkg && typeof pkg === 'object' && pkg.scripts && typeof pkg.scripts === 'object') {
|
|
41
|
+
return { status: 'ok', scripts: pkg.scripts }
|
|
42
|
+
}
|
|
43
|
+
return { status: 'no-scripts', scripts: null }
|
|
44
|
+
} catch {
|
|
45
|
+
return { status: 'unreadable', scripts: null }
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* 为单条命令计算候选 package.json 路径列表。
|
|
51
|
+
*
|
|
52
|
+
* 规则(blueprint task-02 + design §5 H2):
|
|
53
|
+
* - 有 `cd <subdir> &&` 前缀 → 仅查 `<projectRoot>/<subdir>/package.json`(cd 锁定子目录,不回退根)
|
|
54
|
+
* - 无前缀 + modules 块提供 → 查每个 module.path 的子包 + 根 package.json(多候选任一命中即视为存在)
|
|
55
|
+
* - 无前缀 + 无 modules → 仅查 `<projectRoot>/package.json`
|
|
56
|
+
*
|
|
57
|
+
* @param {string} text 全文
|
|
58
|
+
* @param {number} matchIndex 当前命令在 text 中的起始位置
|
|
59
|
+
* @param {string} projectRoot
|
|
60
|
+
* @param {object|null|undefined} modules local.yaml modules 块({ name: { path, test } })
|
|
61
|
+
* @returns {string[]} 候选 package.json 绝对路径列表(至少 1 个)
|
|
62
|
+
*/
|
|
63
|
+
function resolveCandidates(text, matchIndex, projectRoot, modules) {
|
|
64
|
+
const before = text.slice(0, matchIndex)
|
|
65
|
+
const cdMatch = before.match(CD_PREFIX_RE)
|
|
66
|
+
if (cdMatch) {
|
|
67
|
+
const subdir = cdMatch[1].replace(/^["']|["']$/g, '')
|
|
68
|
+
return [join(projectRoot, subdir, 'package.json')]
|
|
69
|
+
}
|
|
70
|
+
if (modules && typeof modules === 'object') {
|
|
71
|
+
const candidates = []
|
|
72
|
+
for (const [, mod] of Object.entries(modules)) {
|
|
73
|
+
if (mod && typeof mod === 'object' && typeof mod.path === 'string' && mod.path.trim() !== '') {
|
|
74
|
+
candidates.push(join(projectRoot, mod.path, 'package.json'))
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
candidates.push(join(projectRoot, 'package.json'))
|
|
78
|
+
return candidates
|
|
79
|
+
}
|
|
80
|
+
return [join(projectRoot, 'package.json')]
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* 校验文本中所有 `npm|pnpm|yarn run <script>` 命令在 package.json scripts 中存在(monorepo 感知)。
|
|
85
|
+
*
|
|
86
|
+
* @param {string} text 待校验文本(local.yaml 内容 / TaskCard verify+implementation 字段等)
|
|
87
|
+
* @param {{ projectRoot: string, modules?: object|null }} opts
|
|
88
|
+
* - projectRoot : 项目根目录绝对路径
|
|
89
|
+
* - modules : local.yaml modules 块(可选),结构如 `{ frontend: { path: "frontend/", test: "..." } }`
|
|
90
|
+
* @returns {{ invalid: Array<{ cmd: string, reason: string }>, checked: number }}
|
|
91
|
+
* - invalid : 找不到 script 的命令列表,每项含 { cmd, reason };reason 区分
|
|
92
|
+
* 「package.json 不存在」「package.json 解析失败」「package.json 无 <script> script」
|
|
93
|
+
* - checked : 从 text 提取到的命令总数(含合法与非法)
|
|
94
|
+
*/
|
|
95
|
+
export function validateScriptCommands(text, { projectRoot, modules } = {}) {
|
|
96
|
+
const invalid = []
|
|
97
|
+
if (!text || typeof text !== 'string') {
|
|
98
|
+
return { invalid, checked: 0 }
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const matches = [...text.matchAll(SCRIPT_CMD_RE)]
|
|
102
|
+
const checked = matches.length
|
|
103
|
+
|
|
104
|
+
for (const m of matches) {
|
|
105
|
+
const pm = m[1]
|
|
106
|
+
const scriptName = m[2]
|
|
107
|
+
const cmd = `${pm} run ${scriptName}`
|
|
108
|
+
|
|
109
|
+
const candidates = resolveCandidates(text, m.index ?? 0, projectRoot, modules)
|
|
110
|
+
|
|
111
|
+
let found = false
|
|
112
|
+
let existedAny = false // 至少一个候选文件存在且可解析(ok 或 no-scripts)
|
|
113
|
+
let parseFailedAny = false // 至少一个候选文件解析失败(unreadable)
|
|
114
|
+
for (const pkgPath of candidates) {
|
|
115
|
+
const result = readScripts(pkgPath)
|
|
116
|
+
if (result.status === 'ok') {
|
|
117
|
+
existedAny = true
|
|
118
|
+
if (Object.prototype.hasOwnProperty.call(result.scripts, scriptName)) {
|
|
119
|
+
found = true
|
|
120
|
+
break
|
|
121
|
+
}
|
|
122
|
+
} else if (result.status === 'no-scripts') {
|
|
123
|
+
existedAny = true
|
|
124
|
+
// scripts 字段缺失 → 该候选肯定不含此 script,继续看其他候选
|
|
125
|
+
} else if (result.status === 'unreadable') {
|
|
126
|
+
parseFailedAny = true
|
|
127
|
+
}
|
|
128
|
+
// missing → 既不算 existed 也不算 parseFailed
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (!found) {
|
|
132
|
+
let reason
|
|
133
|
+
if (!existedAny && parseFailedAny) {
|
|
134
|
+
reason = `package.json 解析失败: ${candidates.join(', ')}`
|
|
135
|
+
} else if (!existedAny) {
|
|
136
|
+
reason = `package.json 不存在: ${candidates.join(', ')}`
|
|
137
|
+
} else {
|
|
138
|
+
reason = `package.json 无 ${scriptName} script`
|
|
139
|
+
}
|
|
140
|
+
invalid.push({ cmd, reason })
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return { invalid, checked }
|
|
145
|
+
}
|