@plainconceptsplatform/agent-harness 3.1.0 → 3.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.
- package/README.md +452 -434
- package/cli/commands/update.js +1 -1
- package/cli/fragments/archive/az.md +97 -97
- package/cli/fragments/archive/gh.md +96 -96
- package/cli/fragments/archive/gl.md +96 -96
- package/cli/fragments/archive/none.md +75 -75
- package/cli/fragments/guardrails/codegraph.md +5 -5
- package/cli/fragments/guardrails/humanizer.md +4 -4
- package/cli/fragments/guardrails/memory.md +4 -4
- package/cli/fragments/guardrails/rtk.md +3 -3
- package/cli/fragments/guardrails/simple-english.md +4 -4
- package/cli/fragments/ops-ship/az.md +81 -81
- package/cli/index.js +0 -0
- package/cli/presets/agents-content.json +34 -34
- package/cli/presets/browser.json +10 -10
- package/cli/steps/browser/index.js +48 -48
- package/cli/steps/copy/commands.js +79 -79
- package/cli/steps/copy/index.js +88 -88
- package/cli/steps/copy/skills.js +307 -307
- package/cli/steps/metadata/index.js +5 -2
- package/cli/steps/models/write.js +5 -1
- package/cli/utils/copy.js +5 -2
- package/cli/utils/paths.js +80 -80
- package/harness/.opencode/_gitignore +9 -9
- package/harness/.opencode/commands/init.md +5 -5
- package/harness/.opencode/commands/make-architecture.md +5 -5
- package/harness/.opencode/commands/make-design.md +5 -5
- package/harness/.opencode/commands/make-engineer.md +5 -5
- package/harness/.opencode/commands/make-guardrails.md +5 -5
- package/harness/.opencode/commands/make-user-model.md +5 -5
- package/harness/.opencode/commands/plan-apply.md +9 -9
- package/harness/.opencode/commands/plan-goal.md +5 -5
- package/harness/.opencode/commands/plan-quick.md +5 -5
- package/harness/.opencode/commands/plan-story.md +9 -9
- package/harness/.opencode/commands/repo-audit.md +5 -5
- package/harness/.opencode/commands/repo-initialize.md +5 -5
- package/harness/.opencode/commands/repo-onboard.md +5 -5
- package/harness/.opencode/commands/repo-verify.md +5 -5
- package/harness/.opencode/scripts/check-diff.mjs +225 -0
- package/harness/.opencode/scripts/code-rules.mjs +280 -0
- package/harness/AGENTS.md +49 -49
- package/package.json +12 -14
- package/skills/README.md +18 -18
- package/skills/browser-automation/SKILL.md +72 -72
- package/skills/pc-guardrails-generic/SKILL.md +2 -1
- package/skills/pc-make-architecture/SKILL.md +32 -32
- package/skills/pc-make-design/SKILL.md +45 -45
- package/skills/pc-make-engineer/signal-mapping.md +53 -53
- package/skills/pc-make-guardrails/SKILL.md +44 -44
- package/skills/pc-make-user-model/SKILL.md +56 -56
- package/skills/pc-plan-apply/SKILL.md +1 -1
- package/skills/pc-plan-apply/simple-mode.md +21 -21
- package/skills/pc-plan-archive/SKILL.md +66 -66
- package/skills/pc-plan-goal/branching.md +39 -39
- package/skills/pc-plan-goal/output.md +71 -71
- package/skills/pc-plan-quick/SKILL.md +46 -46
- package/skills/pc-plan-story/SKILL.md +93 -93
- package/skills/pc-repo-help/SKILL.md +87 -87
- package/skills/pc-repo-initialize/SKILL.md +119 -119
- package/skills/pc-repo-onboard/SKILL.md +32 -32
- package/skills/pc-repo-verify/SKILL.md +150 -150
- package/skills/pc-userstory-az/SKILL.md +71 -71
- package/skills/pc-userstory-browser/SKILL.md +64 -64
- package/skills/pc-userstory-gh/SKILL.md +63 -63
- package/skills/pc-userstory-jira/SKILL.md +74 -74
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
// The same rules Project-Health uses to score a repository (collector/src/metrics/code-rules.ts).
|
|
2
|
+
// Keep the two in step: a rule that differs makes the gate and the dashboard disagree.
|
|
3
|
+
|
|
4
|
+
const SOURCE = /\.(?:[cm]?[jt]sx?|cs)$/
|
|
5
|
+
const SKIP =
|
|
6
|
+
/(?:^|\/)(?:node_modules|dist|build|out|\.next|bin|obj|coverage|Migrations|migrations|wwwroot|vendor|generated|__generated__|\.github|\.opencode|\.agents|\.claude|openspec)(?:\/|$)|(?:^|\/)components\/ui\/|\.d\.ts$|\.g\.cs$|\.Designer\.cs$|\.min\.js$|AssemblyInfo\.cs$/
|
|
7
|
+
const TEST =
|
|
8
|
+
/(?:^|\/)(?:tests?|__tests__|e2e|specs?)\/|\.(?:test|spec)\.[cm]?[jt]sx?$|(?:^|\/)[\w.]+\.(?:Unit|Integration|E2E|Acceptance|Architecture)?Tests?\/|Tests?\.cs$/
|
|
9
|
+
const GENERATED_HEADER =
|
|
10
|
+
/Managed by @plainconceptsplatform|<auto-generated|@generated|This file was (?:auto-)?generated/i
|
|
11
|
+
|
|
12
|
+
export function kindOf(path) {
|
|
13
|
+
if (!SOURCE.test(path) || SKIP.test(path)) return null
|
|
14
|
+
return TEST.test(path) ? 'test' : 'source'
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function isGenerated(text) {
|
|
18
|
+
return GENERATED_HEADER.test(text.split('\n', 4).join('\n'))
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function lineKinds(text) {
|
|
22
|
+
const kinds = []
|
|
23
|
+
let inBlock = false
|
|
24
|
+
for (const raw of text.split('\n')) {
|
|
25
|
+
const t = raw.trim()
|
|
26
|
+
if (!t) kinds.push('blank')
|
|
27
|
+
else if (inBlock) {
|
|
28
|
+
kinds.push('comment')
|
|
29
|
+
if (t.includes('*/')) inBlock = false
|
|
30
|
+
} else if (t.startsWith('/*')) {
|
|
31
|
+
kinds.push('comment')
|
|
32
|
+
inBlock = !t.includes('*/')
|
|
33
|
+
} else if (t.startsWith('//')) kinds.push('comment')
|
|
34
|
+
else kinds.push('code')
|
|
35
|
+
}
|
|
36
|
+
return kinds
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function countLines(text) {
|
|
40
|
+
let codeLines = 0
|
|
41
|
+
let commentLines = 0
|
|
42
|
+
for (const kind of lineKinds(text)) {
|
|
43
|
+
if (kind === 'code') codeLines += 1
|
|
44
|
+
else if (kind === 'comment') commentLines += 1
|
|
45
|
+
}
|
|
46
|
+
return { codeLines, commentLines }
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const FILLER = new Set(
|
|
50
|
+
'a an the this that of for to in on is are be or and with by from its it get gets set sets getting setting value values instance class record struct interface enum new initializes initialises creates constructor property field method current specified given provided object function component hook type props'.split(' '),
|
|
51
|
+
)
|
|
52
|
+
const RETURNS_FILLER = new Set([
|
|
53
|
+
...FILLER,
|
|
54
|
+
'result', 'results', 'task', 'representing', 'asynchronous', 'async', 'operation', 'returns', 'returned', 'which', 'that',
|
|
55
|
+
])
|
|
56
|
+
|
|
57
|
+
const words = text =>
|
|
58
|
+
text
|
|
59
|
+
.replace(/<see\s+cref="([^"]+)"\s*\/>/g, ' $1 ')
|
|
60
|
+
.replace(/<paramref\s+name="([^"]+)"\s*\/>/g, ' $1 ')
|
|
61
|
+
.replace(/\{@link\s+([^}\s]+)[^}]*\}/g, ' $1 ')
|
|
62
|
+
.replace(/<[^>]+>/g, ' ')
|
|
63
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1 $2')
|
|
64
|
+
.replace(/([A-Z])([A-Z][a-z])/g, '$1 $2')
|
|
65
|
+
.toLowerCase()
|
|
66
|
+
.split(/[^a-z0-9]+/)
|
|
67
|
+
.filter(Boolean)
|
|
68
|
+
|
|
69
|
+
const stem = w => (w.length > 3 && w.endsWith('s') && !w.endsWith('ss') ? w.slice(0, -1) : w)
|
|
70
|
+
|
|
71
|
+
export function restates(text, identifier, filler = FILLER) {
|
|
72
|
+
const name = new Set(words(identifier.replace(/^I(?=[A-Z])/, '')).map(stem))
|
|
73
|
+
const said = words(text).filter(w => !filler.has(w) && !filler.has(stem(w))).map(stem)
|
|
74
|
+
return said.every(w => name.has(w))
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const DECL_KEYWORD = /\b(?:class|record|struct|interface|enum|namespace|type|function)\s+([A-Za-z_$][\w$]*)/
|
|
78
|
+
const DECL_VAR = /\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)/
|
|
79
|
+
const DECL_CALL = /([A-Za-z_$][\w$]*)\s*(?:<[^()]*>)?\s*\(/
|
|
80
|
+
const DECL_PROP = /([A-Za-z_$][\w$]*)\s*[?!]?\s*(?:\{|=>|=|;|:|$)/
|
|
81
|
+
|
|
82
|
+
export function declaredName(line) {
|
|
83
|
+
const l = line.trim()
|
|
84
|
+
if (!l || l.startsWith('[') || l.startsWith('@')) return null
|
|
85
|
+
return (l.match(DECL_KEYWORD) ?? l.match(DECL_VAR) ?? l.match(DECL_CALL) ?? l.match(DECL_PROP))?.[1] ?? null
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const BANNER = /^\/\/+\s*(?:([=\-*#~_+])\1{2,}.*|.*([=\-*#~_+])\2{2,})\s*$/
|
|
89
|
+
const REGION = /^#(?:region|endregion)\b/
|
|
90
|
+
const TODO = /\/\/.*\b(?:TODO|FIXME|HACK|XXX)\b/
|
|
91
|
+
const TRACKED = /#\d+|https?:\/\/|\b[A-Z][A-Z0-9]+-\d+\b|\(\s*issue\s/i
|
|
92
|
+
const APOLOGETIC =
|
|
93
|
+
/\b(?:not sure (?:if|whether|this|why)|hope this works|might not work|probably wrong|no idea why|i think this (?:works|is right)|quick and dirty)\b/i
|
|
94
|
+
const CODE_BODY =
|
|
95
|
+
/^(?:(?:var|let|const|await|return|using|import|export|if|else|for|foreach|while|switch|throw|new|public|private|protected|internal|static|async)\b.*[;{(]\s*$|[\w.$[\]]+\s*(?:\+\+|--|[+\-*/]?=)\s*[^=].*;\s*$|[\w.$]+(?:<[^>]*>)?\([^)]*\)\s*;\s*$|[{}]\s*;?\s*$|\}\s*else\b.*$)/
|
|
96
|
+
const TOOL_PRAGMA =
|
|
97
|
+
/^(?:nosemgrep|eslint|biome|jscpd|@ts-|prettier|istanbul|c8|v8|webpack|vite|ReSharper|resharper|language=|<auto-generated|#pragma)/
|
|
98
|
+
|
|
99
|
+
const bodyOf = trimmed => trimmed.replace(/^\/\/+\s?/, '')
|
|
100
|
+
|
|
101
|
+
export function trailingComment(line) {
|
|
102
|
+
let quote = null
|
|
103
|
+
for (let i = 0; i < line.length; i++) {
|
|
104
|
+
const c = line[i]
|
|
105
|
+
if (quote) {
|
|
106
|
+
if (c === '\\') i += 1
|
|
107
|
+
else if (c === quote) quote = null
|
|
108
|
+
} else if (c === "'" || c === '"' || c === '`') quote = c
|
|
109
|
+
else if (c === '/' && line[i + 1] === '/' && line[i - 1] !== ':') return line.slice(i)
|
|
110
|
+
}
|
|
111
|
+
return null
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function slopFindings(text) {
|
|
115
|
+
const lines = text.split('\n')
|
|
116
|
+
const out = []
|
|
117
|
+
const add = (rule, line) => out.push({ rule, line })
|
|
118
|
+
const nameAfter = from => {
|
|
119
|
+
for (let k = from; k < lines.length && k < from + 6; k++) {
|
|
120
|
+
const name = declaredName(lines[k])
|
|
121
|
+
if (name) return name
|
|
122
|
+
}
|
|
123
|
+
return null
|
|
124
|
+
}
|
|
125
|
+
let codeRun = false
|
|
126
|
+
for (let i = 0; i < lines.length; i++) {
|
|
127
|
+
const t = lines[i].trim()
|
|
128
|
+
const isLineComment = t.startsWith('//') && !t.startsWith('///')
|
|
129
|
+
const body = isLineComment ? bodyOf(t).trim() : ''
|
|
130
|
+
const isCode =
|
|
131
|
+
isLineComment && body !== '' && !TOOL_PRAGMA.test(body) && CODE_BODY.test(body) && !/\s(?:https?:)?\/\//.test(body)
|
|
132
|
+
if (isCode && !codeRun) add('commented-out-code', i + 1)
|
|
133
|
+
codeRun = isCode || (codeRun && isLineComment && !body)
|
|
134
|
+
|
|
135
|
+
if (isLineComment && BANNER.test(t)) add('banner', i + 1)
|
|
136
|
+
if (REGION.test(t)) add('region', i + 1)
|
|
137
|
+
const comment = trailingComment(t)
|
|
138
|
+
if (comment && TODO.test(comment) && !TRACKED.test(comment)) add('untracked-todo', i + 1)
|
|
139
|
+
if ((t.startsWith('//') || t.startsWith('*') || t.startsWith('/*')) && APOLOGETIC.test(t)) add('apologetic', i + 1)
|
|
140
|
+
|
|
141
|
+
if (t.startsWith('///') && t.includes('<summary>')) {
|
|
142
|
+
const start = i
|
|
143
|
+
let doc = ''
|
|
144
|
+
let j = i
|
|
145
|
+
for (; j < lines.length && lines[j].trim().startsWith('///'); j++) {
|
|
146
|
+
doc += ` ${lines[j].trim().replace(/^\/\/\/\s?/, '')}`
|
|
147
|
+
}
|
|
148
|
+
const name = nameAfter(j)
|
|
149
|
+
const summary = doc.match(/<summary>([\s\S]*?)<\/summary>/)?.[1] ?? ''
|
|
150
|
+
if (name && summary.trim() && restates(summary, name)) add('restating-summary', start + 1)
|
|
151
|
+
for (const m of doc.matchAll(/<param\s+name="([^"]+)"\s*>([\s\S]*?)<\/param>/g)) {
|
|
152
|
+
if (m[2].trim() && restates(m[2], m[1])) add('restating-param', start + 1)
|
|
153
|
+
}
|
|
154
|
+
for (const m of doc.matchAll(/<returns>([\s\S]*?)<\/returns>/g)) {
|
|
155
|
+
if (m[1].trim() && restates(m[1], '', RETURNS_FILLER)) add('filler-returns', start + 1)
|
|
156
|
+
}
|
|
157
|
+
i = j - 1
|
|
158
|
+
} else if (t.startsWith('/**') && !t.startsWith('/***')) {
|
|
159
|
+
const start = i
|
|
160
|
+
let j = i
|
|
161
|
+
let doc = ''
|
|
162
|
+
for (; j < lines.length; j++) {
|
|
163
|
+
doc += ` ${lines[j].trim()}`
|
|
164
|
+
if (lines[j].includes('*/')) break
|
|
165
|
+
}
|
|
166
|
+
const text = doc.replace(/^\s*\/\*\*|\*\/\s*$/g, ' ').replace(/\s\*\s?/g, ' ').trim()
|
|
167
|
+
const summary = text.split(/\s@\w+/)[0]?.trim() ?? ''
|
|
168
|
+
const name = nameAfter(j + 1)
|
|
169
|
+
if (name && summary && restates(summary, name)) add('restating-jsdoc', start + 1)
|
|
170
|
+
for (const m of text.matchAll(/@param\s+(?:\{[^}]*\}\s*)?\[?([\w$.]+)\]?\s*(?:-\s*)?([^@]*)/g)) {
|
|
171
|
+
if (m[2].trim() && restates(m[2], m[1])) add('restating-param', start + 1)
|
|
172
|
+
}
|
|
173
|
+
for (const m of text.matchAll(/@returns?\s+(?:\{[^}]*\}\s*)?([^@]*)/g)) {
|
|
174
|
+
if (m[1].trim() && restates(m[1], '', RETURNS_FILLER)) add('filler-returns', start + 1)
|
|
175
|
+
}
|
|
176
|
+
i = j
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return out
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const ISSUE_REF = /(?:\b[A-Z][\w-]*\s)?#\d+\b|\b[A-Z][A-Z0-9]+-\d+\b/
|
|
183
|
+
const PAST =
|
|
184
|
+
/\b(?:was|were|used to|previously|before|had|broke|regressed|ended up|turned out|once|anymore|no longer|caused)\b/i
|
|
185
|
+
const TS_INTERNAL = /^(?:async\s+)?(?:function|const|let|var|class|interface|type|enum)\b/
|
|
186
|
+
const CS_INTERNAL = /^(?:private|internal|private\s+protected)\b/
|
|
187
|
+
|
|
188
|
+
export function commentBlocks(text) {
|
|
189
|
+
const lines = text.split('\n')
|
|
190
|
+
const blocks = []
|
|
191
|
+
const firstCode = lines.findIndex(l => {
|
|
192
|
+
const t = l.trim()
|
|
193
|
+
return t !== '' && !t.startsWith('//') && !t.startsWith('/*') && !t.startsWith('*')
|
|
194
|
+
})
|
|
195
|
+
let i = 0
|
|
196
|
+
while (i < lines.length) {
|
|
197
|
+
const t = lines[i].trim()
|
|
198
|
+
if (!(t.startsWith('//') || t.startsWith('/*'))) {
|
|
199
|
+
i += 1
|
|
200
|
+
continue
|
|
201
|
+
}
|
|
202
|
+
const start = i
|
|
203
|
+
let inBlock = false
|
|
204
|
+
let body = ''
|
|
205
|
+
for (; i < lines.length; i++) {
|
|
206
|
+
const s = lines[i].trim()
|
|
207
|
+
if (inBlock) {
|
|
208
|
+
body += ` ${s}`
|
|
209
|
+
if (s.includes('*/')) inBlock = false
|
|
210
|
+
continue
|
|
211
|
+
}
|
|
212
|
+
if (s.startsWith('/*')) {
|
|
213
|
+
body += ` ${s}`
|
|
214
|
+
inBlock = !s.includes('*/')
|
|
215
|
+
continue
|
|
216
|
+
}
|
|
217
|
+
if (s.startsWith('//')) {
|
|
218
|
+
body += ` ${s}`
|
|
219
|
+
continue
|
|
220
|
+
}
|
|
221
|
+
break
|
|
222
|
+
}
|
|
223
|
+
const rules = []
|
|
224
|
+
if (ISSUE_REF.test(body) && PAST.test(body)) rules.push('history')
|
|
225
|
+
if (i - start >= 5 && start > firstCode) rules.push('essay')
|
|
226
|
+
if (body.trimStart().startsWith('/**') || body.trimStart().startsWith('///')) {
|
|
227
|
+
const next = lines.slice(i, i + 4).find(l => {
|
|
228
|
+
const s = l.trim()
|
|
229
|
+
return s !== '' && !s.startsWith('[') && !s.startsWith('@')
|
|
230
|
+
})
|
|
231
|
+
const decl = next?.trim() ?? ''
|
|
232
|
+
if (TS_INTERNAL.test(decl) || CS_INTERNAL.test(decl)) rules.push('internal-doc')
|
|
233
|
+
}
|
|
234
|
+
blocks.push({ start: start + 1, end: i, rules })
|
|
235
|
+
}
|
|
236
|
+
return blocks
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const TS_CASE =
|
|
240
|
+
/(?:^|[^\w.$])(x|f)?(?:it|test)(\.only|\.skip|\.todo|\.concurrent|\.each\s*(?:\([^)]*\)|`[^`]*`))?\s*\(\s*['"`]/g
|
|
241
|
+
const CS_CASE = /\[(?:Fact|Theory|SkippableFact|SkippableTheory|Test|TestMethod)\b([^\]]*)\]/g
|
|
242
|
+
const TS_ASSERT = /\b(?:expect|assert)\w*(?:\.\w+)?\s*\(|\.should\b/g
|
|
243
|
+
const TS_CALL_ASSERT = /\.(?:not\.)?(?:toHaveBeenCalled\w*|toBeCalled\w*|toHaveBeenNthCalledWith)\s*\(/g
|
|
244
|
+
const CS_ASSERT = /\bAssert\w*\.\w+|\.Should\w*\(|\bVerify\w*\(|\bThrows\w*(?:Async)?\s*[<(]|\.Received\(/g
|
|
245
|
+
const CS_CALL_ASSERT = /\.Received\(|\.DidNotReceive\(|\.Verify\(\s*\w+\s*=>/g
|
|
246
|
+
const SNAPSHOT = /\.toMatch(?:Inline|File)?Snapshot\s*\(|\bawait\s+Verify(?:Json|Xml)?\s*\(/
|
|
247
|
+
|
|
248
|
+
const count = (text, re) => (text.match(re) ?? []).length
|
|
249
|
+
|
|
250
|
+
export function testStats(path, text) {
|
|
251
|
+
const cs = path.endsWith('.cs')
|
|
252
|
+
const starts = []
|
|
253
|
+
for (const m of text.matchAll(cs ? CS_CASE : TS_CASE)) {
|
|
254
|
+
if (cs) {
|
|
255
|
+
starts.push({ index: m.index, skipped: /\bSkip\s*=/.test(m[1] ?? ''), focused: false })
|
|
256
|
+
} else {
|
|
257
|
+
const modifier = m[2] ?? ''
|
|
258
|
+
if (modifier === '.todo') continue
|
|
259
|
+
starts.push({ index: m.index, skipped: m[1] === 'x' || modifier === '.skip', focused: m[1] === 'f' || modifier === '.only' })
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
const stats = { cases: starts.length, skipped: 0, focused: 0, snapshot: 0, noAssertion: 0, mockOnly: 0 }
|
|
263
|
+
if (cs) stats.skipped += count(text, /\[Ignore\b/g)
|
|
264
|
+
else {
|
|
265
|
+
stats.skipped += count(text, /\b(?:describe\.skip|xdescribe)\s*\(/g)
|
|
266
|
+
stats.focused += count(text, /\b(?:describe\.only|fdescribe)\s*\(/g)
|
|
267
|
+
}
|
|
268
|
+
for (let k = 0; k < starts.length; k++) {
|
|
269
|
+
const s = starts[k]
|
|
270
|
+
const segment = text.slice(s.index, starts[k + 1]?.index ?? text.length)
|
|
271
|
+
if (s.skipped) stats.skipped += 1
|
|
272
|
+
if (s.focused) stats.focused += 1
|
|
273
|
+
if (SNAPSHOT.test(segment)) stats.snapshot += 1
|
|
274
|
+
const asserts = count(segment, cs ? CS_ASSERT : TS_ASSERT)
|
|
275
|
+
const callAsserts = count(segment, cs ? CS_CALL_ASSERT : TS_CALL_ASSERT)
|
|
276
|
+
if (asserts === 0) stats.noAssertion += 1
|
|
277
|
+
else if (callAsserts > 0 && callAsserts >= asserts) stats.mockOnly += 1
|
|
278
|
+
}
|
|
279
|
+
return stats
|
|
280
|
+
}
|
package/harness/AGENTS.md
CHANGED
|
@@ -1,49 +1,49 @@
|
|
|
1
|
-
# AGENTS.md
|
|
2
|
-
|
|
3
|
-
<!-- PC-NOT-INITIALIZED -->
|
|
4
|
-
|
|
5
|
-
# Agent operating guide
|
|
6
|
-
|
|
7
|
-
The operating contract for AI agents in this repository. Agent-agnostic: OpenCode, Claude Code, Codex, Gemini and others.
|
|
8
|
-
|
|
9
|
-
## Session context
|
|
10
|
-
|
|
11
|
-
Before a non-trivial change, read `AGENTS.md` for workflow rules, `ARCHITECTURE.md` for boundaries and component interactions, `DESIGN.md` for UI and design-system work, and the active OpenSpec change or the specification covering the area you are changing.
|
|
12
|
-
|
|
13
|
-
Command aliases: OpenSpec skills may reference `/opsx-propose`, `/opsx-apply`, `/opsx-archive`, or `/opsx-explore`. Always substitute them with the `pc-plan-propose`, `pc-plan-apply`, `pc-plan-archive`, and `pc-plan-explore` skills respectively. User-facing command names are `/plan-propose`, `/plan-apply`, `/plan-archive`, and `/plan-explore`. Never mention the `opsx-` names to the user.
|
|
14
|
-
|
|
15
|
-
## Workflow ownership
|
|
16
|
-
|
|
17
|
-
<!-- PC-PLATFORM-WORKFLOW-START -->
|
|
18
|
-
<!-- PC-PLATFORM-WORKFLOW-END -->
|
|
19
|
-
|
|
20
|
-
## Planning and execution
|
|
21
|
-
|
|
22
|
-
- Never combine an unrelated refactor with the work you were asked to do. It arrives under a message that does not mention it, and the reviewer approves both.
|
|
23
|
-
- Never introduce a new convention where the repository already has one, and never guess when requirements, architecture or a security constraint are unclear. Ask.
|
|
24
|
-
|
|
25
|
-
## Engineer selection
|
|
26
|
-
|
|
27
|
-
Inspect `.opencode/agents/*.md` before spawning. Prefer the most specialized custom engineer. `build` and `plan` are the only primaries and are never spawned; `fullstack-engineer` is the body they share and the fallback worker, so prefer a specialist over it. If no specialist matches, tell the user to create one with `/make-engineer`. Spawn only engineers present in that directory.
|
|
28
|
-
|
|
29
|
-
The `pc-plan-apply` skill is authoritative for subagent waves, dependency ordering, retries, and concurrency. A spawn past `agents.maxConcurrent` is denied (`pc-subagent-monitor`); a denied spawn is not a failed task, so re-issue it in the next wave.
|
|
30
|
-
|
|
31
|
-
## Tool and repository safety
|
|
32
|
-
|
|
33
|
-
- Never expose or commit secrets, credentials, tokens, or production data.
|
|
34
|
-
- Never overwrite a generated file, or uncommitted changes you did not make. The tree may be shared with a person and another agent.
|
|
35
|
-
- Never bypass a check, weaken a test, or silence a lint rule to reach a green result. A green run that was arranged is worse than a red one, because nobody looks again.
|
|
36
|
-
- Commit, push, open a pull request, change dependencies, or touch deployment configuration only with the user's explicit approval and the repository's stated process.
|
|
37
|
-
|
|
38
|
-
## Verification and completion
|
|
39
|
-
|
|
40
|
-
- Never call a bug fixed without a test that would have caught it, where one is practical.
|
|
41
|
-
- Never leave a specification, `ARCHITECTURE.md` or `DESIGN.md` asserting something this change made false.
|
|
42
|
-
- Never end on a blocker without naming it and the decision it needs. An unattended run that stops quietly looks like one that finished.
|
|
43
|
-
|
|
44
|
-
## Skills
|
|
45
|
-
|
|
46
|
-
Skills live in `.agents/skills/`. Always installed: `@pc-guardrails-generic`, `@pc-guardrails-project`, and `@browser-automation`. An entry under `## Abilities` is not a passive reference: call the `skill` tool once per `@skill-name`, guardrails first. Editing, shell and spawning stay blocked until you have (`pc-system-reminders`), and a loaded skill can require further loads.
|
|
47
|
-
|
|
48
|
-
<!-- PC-PLATFORM-SKILLS-GUIDE-START -->
|
|
49
|
-
<!-- PC-PLATFORM-SKILLS-GUIDE-END -->
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
<!-- PC-NOT-INITIALIZED -->
|
|
4
|
+
|
|
5
|
+
# Agent operating guide
|
|
6
|
+
|
|
7
|
+
The operating contract for AI agents in this repository. Agent-agnostic: OpenCode, Claude Code, Codex, Gemini and others.
|
|
8
|
+
|
|
9
|
+
## Session context
|
|
10
|
+
|
|
11
|
+
Before a non-trivial change, read `AGENTS.md` for workflow rules, `ARCHITECTURE.md` for boundaries and component interactions, `DESIGN.md` for UI and design-system work, and the active OpenSpec change or the specification covering the area you are changing.
|
|
12
|
+
|
|
13
|
+
Command aliases: OpenSpec skills may reference `/opsx-propose`, `/opsx-apply`, `/opsx-archive`, or `/opsx-explore`. Always substitute them with the `pc-plan-propose`, `pc-plan-apply`, `pc-plan-archive`, and `pc-plan-explore` skills respectively. User-facing command names are `/plan-propose`, `/plan-apply`, `/plan-archive`, and `/plan-explore`. Never mention the `opsx-` names to the user.
|
|
14
|
+
|
|
15
|
+
## Workflow ownership
|
|
16
|
+
|
|
17
|
+
<!-- PC-PLATFORM-WORKFLOW-START -->
|
|
18
|
+
<!-- PC-PLATFORM-WORKFLOW-END -->
|
|
19
|
+
|
|
20
|
+
## Planning and execution
|
|
21
|
+
|
|
22
|
+
- Never combine an unrelated refactor with the work you were asked to do. It arrives under a message that does not mention it, and the reviewer approves both.
|
|
23
|
+
- Never introduce a new convention where the repository already has one, and never guess when requirements, architecture or a security constraint are unclear. Ask.
|
|
24
|
+
|
|
25
|
+
## Engineer selection
|
|
26
|
+
|
|
27
|
+
Inspect `.opencode/agents/*.md` before spawning. Prefer the most specialized custom engineer. `build` and `plan` are the only primaries and are never spawned; `fullstack-engineer` is the body they share and the fallback worker, so prefer a specialist over it. If no specialist matches, tell the user to create one with `/make-engineer`. Spawn only engineers present in that directory.
|
|
28
|
+
|
|
29
|
+
The `pc-plan-apply` skill is authoritative for subagent waves, dependency ordering, retries, and concurrency. A spawn past `agents.maxConcurrent` is denied (`pc-subagent-monitor`); a denied spawn is not a failed task, so re-issue it in the next wave.
|
|
30
|
+
|
|
31
|
+
## Tool and repository safety
|
|
32
|
+
|
|
33
|
+
- Never expose or commit secrets, credentials, tokens, or production data.
|
|
34
|
+
- Never overwrite a generated file, or uncommitted changes you did not make. The tree may be shared with a person and another agent.
|
|
35
|
+
- Never bypass a check, weaken a test, or silence a lint rule to reach a green result. A green run that was arranged is worse than a red one, because nobody looks again.
|
|
36
|
+
- Commit, push, open a pull request, change dependencies, or touch deployment configuration only with the user's explicit approval and the repository's stated process.
|
|
37
|
+
|
|
38
|
+
## Verification and completion
|
|
39
|
+
|
|
40
|
+
- Never call a bug fixed without a test that would have caught it, where one is practical.
|
|
41
|
+
- Never leave a specification, `ARCHITECTURE.md` or `DESIGN.md` asserting something this change made false.
|
|
42
|
+
- Never end on a blocker without naming it and the decision it needs. An unattended run that stops quietly looks like one that finished.
|
|
43
|
+
|
|
44
|
+
## Skills
|
|
45
|
+
|
|
46
|
+
Skills live in `.agents/skills/`. Always installed: `@pc-guardrails-generic`, `@pc-guardrails-project`, and `@browser-automation`. An entry under `## Abilities` is not a passive reference: call the `skill` tool once per `@skill-name`, guardrails first. Editing, shell and spawning stay blocked until you have (`pc-system-reminders`), and a loaded skill can require further loads.
|
|
47
|
+
|
|
48
|
+
<!-- PC-PLATFORM-SKILLS-GUIDE-START -->
|
|
49
|
+
<!-- PC-PLATFORM-SKILLS-GUIDE-END -->
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plainconceptsplatform/agent-harness",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.0",
|
|
4
4
|
"description": "Installs the Plain Concepts Platform Harness into any codebase, and keeps it up to date. Wires OpenCode, OpenSpec, codegraph, and agentmemory into a multi-agent workflow that runs on native parallel subagents.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"opencode",
|
|
@@ -42,7 +42,6 @@
|
|
|
42
42
|
"engines": {
|
|
43
43
|
"node": ">=18"
|
|
44
44
|
},
|
|
45
|
-
"packageManager": "pnpm@11.10.0",
|
|
46
45
|
"dependencies": {
|
|
47
46
|
"@inquirer/prompts": "^7.0.0",
|
|
48
47
|
"chalk": "^5.0.0",
|
|
@@ -51,17 +50,6 @@
|
|
|
51
50
|
"jsonc-parser": "^3.3.1",
|
|
52
51
|
"ora": "^8.0.0"
|
|
53
52
|
},
|
|
54
|
-
"scripts": {
|
|
55
|
-
"lint": "eslint .",
|
|
56
|
-
"lint:fix": "eslint . --fix",
|
|
57
|
-
"lint:md": "markdownlint-cli2 \"**/*.md\"",
|
|
58
|
-
"lint:md:fix": "markdownlint-cli2 --fix \"**/*.md\"",
|
|
59
|
-
"test": "vitest run",
|
|
60
|
-
"test:watch": "vitest",
|
|
61
|
-
"prepublishOnly": "npm run lint && npm run lint:md && npm run test",
|
|
62
|
-
"release:dry": "pnpm publish --dry-run --no-git-checks --access public",
|
|
63
|
-
"release": "pnpm publish --no-git-checks --access public"
|
|
64
|
-
},
|
|
65
53
|
"devDependencies": {
|
|
66
54
|
"@eslint/js": "^9.0.0",
|
|
67
55
|
"eslint": "^9.0.0",
|
|
@@ -71,5 +59,15 @@
|
|
|
71
59
|
},
|
|
72
60
|
"vitest": {
|
|
73
61
|
"environment": "node"
|
|
62
|
+
},
|
|
63
|
+
"scripts": {
|
|
64
|
+
"lint": "eslint .",
|
|
65
|
+
"lint:fix": "eslint . --fix",
|
|
66
|
+
"lint:md": "markdownlint-cli2 \"**/*.md\"",
|
|
67
|
+
"lint:md:fix": "markdownlint-cli2 --fix \"**/*.md\"",
|
|
68
|
+
"test": "vitest run",
|
|
69
|
+
"test:watch": "vitest",
|
|
70
|
+
"release:dry": "pnpm publish --dry-run --no-git-checks --access public",
|
|
71
|
+
"release": "pnpm publish --no-git-checks --access public"
|
|
74
72
|
}
|
|
75
|
-
}
|
|
73
|
+
}
|
package/skills/README.md
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
# skills/
|
|
2
|
-
|
|
3
|
-
Two kinds of skills live here, both discoverable by `npx skills`:
|
|
4
|
-
|
|
5
|
-
| Kind | Directories | Purpose |
|
|
6
|
-
| --- | --- | --- |
|
|
7
|
-
| Installable `pc-*` + `browser-automation` | `pc-*`, `browser-automation` | What the CLI copies into a target project under `.agents/skills/` during onboarding. Plain Concepts' own skills. |
|
|
8
|
-
| CLI-self | `agent-harness-cli` | Skills for agents working on THIS CLI repo (driving `npx @plainconceptsplatform/agent-harness`). Never copied into target projects. |
|
|
9
|
-
|
|
10
|
-
Discoverability: any `SKILL.md` in a subdirectory here is auto-discovered by OpenCode, Claude Code, and `npx skills` (which scans `./skills/` as a recognized project-skill location).
|
|
11
|
-
|
|
12
|
-
## Using one
|
|
13
|
-
|
|
14
|
-
Point your agent at the directory, or copy a skill into wherever your tool discovers skills:
|
|
15
|
-
|
|
16
|
-
```bash
|
|
17
|
-
cp -r skills/agent-harness-cli ~/.claude/skills/
|
|
18
|
-
```
|
|
1
|
+
# skills/
|
|
2
|
+
|
|
3
|
+
Two kinds of skills live here, both discoverable by `npx skills`:
|
|
4
|
+
|
|
5
|
+
| Kind | Directories | Purpose |
|
|
6
|
+
| --- | --- | --- |
|
|
7
|
+
| Installable `pc-*` + `browser-automation` | `pc-*`, `browser-automation` | What the CLI copies into a target project under `.agents/skills/` during onboarding. Plain Concepts' own skills. |
|
|
8
|
+
| CLI-self | `agent-harness-cli` | Skills for agents working on THIS CLI repo (driving `npx @plainconceptsplatform/agent-harness`). Never copied into target projects. |
|
|
9
|
+
|
|
10
|
+
Discoverability: any `SKILL.md` in a subdirectory here is auto-discovered by OpenCode, Claude Code, and `npx skills` (which scans `./skills/` as a recognized project-skill location).
|
|
11
|
+
|
|
12
|
+
## Using one
|
|
13
|
+
|
|
14
|
+
Point your agent at the directory, or copy a skill into wherever your tool discovers skills:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
cp -r skills/agent-harness-cli ~/.claude/skills/
|
|
18
|
+
```
|
|
@@ -1,72 +1,72 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: browser-automation
|
|
3
|
-
description: Reliable, composable browser automation using agent-browser. Use when capturing screenshots of a locally running app, clicking UI elements, reading page content, or automating browser interactions on localhost.
|
|
4
|
-
license: MIT
|
|
5
|
-
compatibility: Requires agent-browser CLI installed (Rust binary) and running.
|
|
6
|
-
metadata:
|
|
7
|
-
author: copilots
|
|
8
|
-
version: "2.0"
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
This skill operates on `localhost` URLs only. Screenshots, clicks, typing, scrolling, and reads on locally running apps are in scope. External services (github.com, dev.azure.com, npmjs.com, etc.) are out of scope for browser tools.
|
|
12
|
-
|
|
13
|
-
The only exception: when the user selects "Others (Browser)" as their backlog platform during onboarding, the `pc-userstory` skill may navigate to work item URLs the user explicitly provides. That is the sole case where external navigation is permitted, and only to URLs the user explicitly gives you.
|
|
14
|
-
|
|
15
|
-
## Best-practice workflow
|
|
16
|
-
|
|
17
|
-
1. Open the page: `agent-browser open <url>` (launches its own Chrome; reuses the running daemon)
|
|
18
|
-
2. Take a snapshot: `agent-browser snapshot` to get the accessibility tree with `@ref` handles
|
|
19
|
-
3. Interact by ref: `agent-browser click @e2`, `agent-browser fill @e3 "text"`
|
|
20
|
-
4. Wait when needed: `agent-browser wait --load networkidle` or `agent-browser wait <selector>`
|
|
21
|
-
5. Confirm the result with a fresh `snapshot`, `get text`, or `screenshot`
|
|
22
|
-
|
|
23
|
-
Refs like `@e2` come from the last snapshot and go stale after page changes: retake the snapshot after navigation or dynamic updates before reusing them. When a click fails because another element covers the target (consent banner, modal), dismiss or interact with the covering element, then take a fresh snapshot before retrying.
|
|
24
|
-
|
|
25
|
-
## Tool access
|
|
26
|
-
|
|
27
|
-
- MCP tools (inside OpenCode): `agent_browser_open`, `agent_browser_snapshot`, `agent_browser_click`, `agent_browser_fill`, `agent_browser_type`, `agent_browser_wait_for_selector`, `agent_browser_screenshot`, `agent_browser_eval`, `agent_browser_close` — configured in `opencode.jsonc` with `--tools core`
|
|
28
|
-
- CLI (any agent, any host): every command below works identically via bash
|
|
29
|
-
- Diagnose the install: `agent-browser doctor`
|
|
30
|
-
|
|
31
|
-
## Selecting elements
|
|
32
|
-
|
|
33
|
-
- Prefer refs from `snapshot` (`@e1`, `@e2`, ...) over CSS selectors
|
|
34
|
-
- CSS selectors also work: `agent-browser click "#submit"`, `agent-browser fill "#email" "text"`
|
|
35
|
-
- Semantic locators: `agent-browser find role button click --name "Submit"`, `find text "Sign In" click`, `find label "Email" fill "value"`, `find testid <id> click`
|
|
36
|
-
- Native selects: `agent-browser select <sel> <value>`
|
|
37
|
-
|
|
38
|
-
## Reading state
|
|
39
|
-
|
|
40
|
-
- `agent-browser get text <sel>` — text content of an element
|
|
41
|
-
- `agent-browser get value <sel>` — input value
|
|
42
|
-
- `agent-browser snapshot` — structured accessibility tree (preferred for parsing)
|
|
43
|
-
- `agent-browser screenshot [path]` — `--full` for full page, `--annotate` for numbered labels
|
|
44
|
-
- `agent-browser read` — agent-readable markdown of the active tab (no Chrome launch for URL reads)
|
|
45
|
-
|
|
46
|
-
## Tabs and sessions
|
|
47
|
-
|
|
48
|
-
- `agent-browser tab` lists tabs with stable `t1`, `t2` ids; `tab new --label <name>` and `tab <label>` switch
|
|
49
|
-
- `agent-browser close` closes the browser; `close --all` closes every session
|
|
50
|
-
|
|
51
|
-
## CLI-first debugging
|
|
52
|
-
|
|
53
|
-
- `agent-browser doctor` — diagnose install, Chrome, and daemon state
|
|
54
|
-
- `agent-browser console` — view console messages; `agent-browser errors` — uncaught exceptions
|
|
55
|
-
- `agent-browser eval "<js>"` — run JavaScript in the page
|
|
56
|
-
- `agent-browser network requests --filter api` — inspect tracked requests
|
|
57
|
-
|
|
58
|
-
## Troubleshooting
|
|
59
|
-
|
|
60
|
-
- Stale ref: take a fresh `snapshot` after any navigation or DOM change
|
|
61
|
-
- Covered click: the error names the covering element; dismiss it, re-snapshot, retry
|
|
62
|
-
- Selector fails: confirm the content exists with `agent-browser get text` or a snapshot before retrying
|
|
63
|
-
- Daemon or Chrome issues: run `agent-browser doctor`
|
|
64
|
-
|
|
65
|
-
## Scope
|
|
66
|
-
|
|
67
|
-
- Screenshots of locally running app on `localhost` URLs: in scope
|
|
68
|
-
- Click, type, scroll, read on `localhost` pages: in scope
|
|
69
|
-
- Navigate to work item URLs the user explicitly provides (when backlog platform is "Others (Browser)"): in scope
|
|
70
|
-
- Navigate to external services for non-work-item purposes: out of scope
|
|
71
|
-
- DevOps or GitHub CLI operations via browser tools: out of scope
|
|
72
|
-
- Reading or modifying production systems via browser tools: out of scope
|
|
1
|
+
---
|
|
2
|
+
name: browser-automation
|
|
3
|
+
description: Reliable, composable browser automation using agent-browser. Use when capturing screenshots of a locally running app, clicking UI elements, reading page content, or automating browser interactions on localhost.
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: Requires agent-browser CLI installed (Rust binary) and running.
|
|
6
|
+
metadata:
|
|
7
|
+
author: copilots
|
|
8
|
+
version: "2.0"
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
This skill operates on `localhost` URLs only. Screenshots, clicks, typing, scrolling, and reads on locally running apps are in scope. External services (github.com, dev.azure.com, npmjs.com, etc.) are out of scope for browser tools.
|
|
12
|
+
|
|
13
|
+
The only exception: when the user selects "Others (Browser)" as their backlog platform during onboarding, the `pc-userstory` skill may navigate to work item URLs the user explicitly provides. That is the sole case where external navigation is permitted, and only to URLs the user explicitly gives you.
|
|
14
|
+
|
|
15
|
+
## Best-practice workflow
|
|
16
|
+
|
|
17
|
+
1. Open the page: `agent-browser open <url>` (launches its own Chrome; reuses the running daemon)
|
|
18
|
+
2. Take a snapshot: `agent-browser snapshot` to get the accessibility tree with `@ref` handles
|
|
19
|
+
3. Interact by ref: `agent-browser click @e2`, `agent-browser fill @e3 "text"`
|
|
20
|
+
4. Wait when needed: `agent-browser wait --load networkidle` or `agent-browser wait <selector>`
|
|
21
|
+
5. Confirm the result with a fresh `snapshot`, `get text`, or `screenshot`
|
|
22
|
+
|
|
23
|
+
Refs like `@e2` come from the last snapshot and go stale after page changes: retake the snapshot after navigation or dynamic updates before reusing them. When a click fails because another element covers the target (consent banner, modal), dismiss or interact with the covering element, then take a fresh snapshot before retrying.
|
|
24
|
+
|
|
25
|
+
## Tool access
|
|
26
|
+
|
|
27
|
+
- MCP tools (inside OpenCode): `agent_browser_open`, `agent_browser_snapshot`, `agent_browser_click`, `agent_browser_fill`, `agent_browser_type`, `agent_browser_wait_for_selector`, `agent_browser_screenshot`, `agent_browser_eval`, `agent_browser_close` — configured in `opencode.jsonc` with `--tools core`
|
|
28
|
+
- CLI (any agent, any host): every command below works identically via bash
|
|
29
|
+
- Diagnose the install: `agent-browser doctor`
|
|
30
|
+
|
|
31
|
+
## Selecting elements
|
|
32
|
+
|
|
33
|
+
- Prefer refs from `snapshot` (`@e1`, `@e2`, ...) over CSS selectors
|
|
34
|
+
- CSS selectors also work: `agent-browser click "#submit"`, `agent-browser fill "#email" "text"`
|
|
35
|
+
- Semantic locators: `agent-browser find role button click --name "Submit"`, `find text "Sign In" click`, `find label "Email" fill "value"`, `find testid <id> click`
|
|
36
|
+
- Native selects: `agent-browser select <sel> <value>`
|
|
37
|
+
|
|
38
|
+
## Reading state
|
|
39
|
+
|
|
40
|
+
- `agent-browser get text <sel>` — text content of an element
|
|
41
|
+
- `agent-browser get value <sel>` — input value
|
|
42
|
+
- `agent-browser snapshot` — structured accessibility tree (preferred for parsing)
|
|
43
|
+
- `agent-browser screenshot [path]` — `--full` for full page, `--annotate` for numbered labels
|
|
44
|
+
- `agent-browser read` — agent-readable markdown of the active tab (no Chrome launch for URL reads)
|
|
45
|
+
|
|
46
|
+
## Tabs and sessions
|
|
47
|
+
|
|
48
|
+
- `agent-browser tab` lists tabs with stable `t1`, `t2` ids; `tab new --label <name>` and `tab <label>` switch
|
|
49
|
+
- `agent-browser close` closes the browser; `close --all` closes every session
|
|
50
|
+
|
|
51
|
+
## CLI-first debugging
|
|
52
|
+
|
|
53
|
+
- `agent-browser doctor` — diagnose install, Chrome, and daemon state
|
|
54
|
+
- `agent-browser console` — view console messages; `agent-browser errors` — uncaught exceptions
|
|
55
|
+
- `agent-browser eval "<js>"` — run JavaScript in the page
|
|
56
|
+
- `agent-browser network requests --filter api` — inspect tracked requests
|
|
57
|
+
|
|
58
|
+
## Troubleshooting
|
|
59
|
+
|
|
60
|
+
- Stale ref: take a fresh `snapshot` after any navigation or DOM change
|
|
61
|
+
- Covered click: the error names the covering element; dismiss it, re-snapshot, retry
|
|
62
|
+
- Selector fails: confirm the content exists with `agent-browser get text` or a snapshot before retrying
|
|
63
|
+
- Daemon or Chrome issues: run `agent-browser doctor`
|
|
64
|
+
|
|
65
|
+
## Scope
|
|
66
|
+
|
|
67
|
+
- Screenshots of locally running app on `localhost` URLs: in scope
|
|
68
|
+
- Click, type, scroll, read on `localhost` pages: in scope
|
|
69
|
+
- Navigate to work item URLs the user explicitly provides (when backlog platform is "Others (Browser)"): in scope
|
|
70
|
+
- Navigate to external services for non-work-item purposes: out of scope
|
|
71
|
+
- DevOps or GitHub CLI operations via browser tools: out of scope
|
|
72
|
+
- Reading or modifying production systems via browser tools: out of scope
|
|
@@ -15,7 +15,7 @@ The marker sections below name the optimization skills this project selected. Lo
|
|
|
15
15
|
|
|
16
16
|
## Code
|
|
17
17
|
|
|
18
|
-
-
|
|
18
|
+
- Zero comments by default. Write one only for what the code cannot show and we do not control: a vendor bug, an external contract, a legal rule. Leave every file you touch with no comment slop, old or new.
|
|
19
19
|
- Never add a file that collects unrelated things — `constants.js`, `types.ts`, `utils.ts`. One responsibility per file, split by domain. A file importing from many unrelated modules is already the symptom.
|
|
20
20
|
|
|
21
21
|
## Temporary files
|
|
@@ -28,6 +28,7 @@ The marker sections below name the optimization skills this project selected. Lo
|
|
|
28
28
|
- Never write more tests than the change requires. A bug fix gets one regression test. A new function gets tests for its public contract, not every internal branch. Never aim for a coverage number.
|
|
29
29
|
- Never test framework or library internals. Test your code's contract, not that EF Core saves a tracked entity.
|
|
30
30
|
- Never generate integration, e2e, or snapshot tests unless explicitly asked. Mock nothing you do not own — use a fake that records state. Never add a test that duplicates a type-check or linter rule.
|
|
31
|
+
- `node .opencode/scripts/check-diff.mjs` enforces the comment and test rules on the diff and must exit 0.
|
|
31
32
|
|
|
32
33
|
<!-- PC-GUARDRAILS-RTK-START -->
|
|
33
34
|
<!-- PC-GUARDRAILS-RTK-END -->
|