@1aboveio/skills 1.0.13 → 1.0.15
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/.codex-plugin/plugin.json +1 -1
- package/.codex-plugin/resources.json +157 -154
- package/README.md +1 -1
- package/package.json +5 -5
- package/skills/app-dev/airflow/SKILL.md +1 -1
- package/skills/app-dev/android-development/SKILL.md +127 -0
- package/skills/app-dev/android-development/references/android-conventions.md +117 -0
- package/skills/app-dev/{android-foundation → android-development}/references/ci.md +10 -6
- package/skills/app-dev/{android-foundation → android-development}/references/diagnosis.md +44 -15
- package/skills/app-dev/android-development/references/feature-work.md +186 -0
- package/skills/app-dev/android-development/references/framework-choice.md +88 -0
- package/skills/app-dev/{android-foundation → android-development}/references/gotchas.md +43 -6
- package/skills/app-dev/android-development/references/init.md +126 -0
- package/skills/app-dev/android-development/references/setup.md +389 -0
- package/skills/app-dev/android-development/references/workspace.md +156 -0
- package/skills/app-dev/android-development/scripts/init.mjs +477 -0
- package/skills/app-dev/app-debug/SKILL.md +1 -1
- package/skills/app-dev/better-auth/SKILL.md +1 -1
- package/skills/app-dev/data-catalog/SKILL.md +53 -16
- package/skills/app-dev/data-catalog/scripts/persist-conventions-core.mjs +231 -21
- package/skills/app-dev/data-catalog/scripts/persist-conventions.mjs +12 -1
- package/skills/app-dev/data-quality/SKILL.md +1 -1
- package/skills/app-dev/ios-foundation/SKILL.md +2 -2
- package/skills/app-dev/monorepo/SKILL.md +1 -1
- package/skills/app-dev/nextjs-fullstack/SKILL.md +1 -1
- package/skills/app-dev/prisma-setup/SKILL.md +1 -1
- package/skills/app-dev/pyspark/SKILL.md +1 -1
- package/skills/app-dev/python-backend/SKILL.md +1 -1
- package/skills/app-dev/shadcn/SKILL.md +1 -1
- package/skills/app-dev/zod-v4/SKILL.md +1 -1
- package/skills/cicd/cloud-build/SKILL.md +1 -1
- package/skills/cicd/cloud-debug/SKILL.md +1 -1
- package/skills/cicd/cloud-deploy/SKILL.md +1 -1
- package/skills/cicd/docker/SKILL.md +1 -1
- package/skills/cicd/github-actions/SKILL.md +1 -1
- package/skills/cicd/github-flow/SKILL.md +10 -1
- package/skills/cicd/github-flow/scripts/persist-conventions-core.mjs +231 -21
- package/skills/cicd/github-flow/scripts/persist-conventions.mjs +18 -1
- package/skills/cicd/google-cloud/SKILL.md +1 -1
- package/skills/cicd/mergify/SKILL.md +1 -1
- package/skills/cicd/podman/SKILL.md +1 -1
- package/skills/content/article-review/SKILL.md +1 -1
- package/skills/content/article-to-image/SKILL.md +1 -1
- package/skills/content/flowchart-svg/SKILL.md +1 -1
- package/skills/content/image-gen/SKILL.md +1 -1
- package/skills/content/mermaid/SKILL.md +1 -1
- package/skills/content/wechat-article-writer/SKILL.md +1 -1
- package/skills/content/wechat-publisher/SKILL.md +1 -1
- package/skills/content/wechat-search/SKILL.md +1 -1
- package/skills/content/xai-search/SKILL.md +1 -1
- package/skills/core/distill-writing/SKILL.md +7 -1
- package/skills/core/doc-to-md/SKILL.md +1 -1
- package/skills/core/editorial-review/SKILL.md +1 -1
- package/skills/core/house-style/SKILL.md +1 -1
- package/skills/core/house-style/references/persistence.md +8 -3
- package/skills/core/house-style/scripts/init-guidance.mjs +1 -1
- package/skills/core/plain-language/SKILL.md +1 -1
- package/skills/core/polish-cn/SKILL.md +1 -1
- package/skills/core/reg-parser/SKILL.md +1 -1
- package/skills/core/terminology/SKILL.md +1 -1
- package/skills/core/terminology/references/init.md +11 -0
- package/skills/core/write-doc/SKILL.md +1 -1
- package/skills/core/write-report/SKILL.md +1 -1
- package/skills/engineering/better-review/SKILL.md +1 -1
- package/skills/engineering/e2e-test/SKILL.md +1 -1
- package/skills/engineering/engineering/SKILL.md +25 -4
- package/skills/engineering/engineering/references/init-scope.md +10 -3
- package/skills/engineering/engineering/references/scope-management.md +11 -0
- package/skills/engineering/engineering/scripts/init.mjs +65 -17
- package/skills/engineering/engineering/scripts/persist-conventions-core.mjs +231 -21
- package/skills/engineering/engineering/scripts/persist-conventions.mjs +48 -21
- package/skills/engineering/engineering-runtime/SKILL.md +1 -1
- package/skills/engineering/engineering-runtime/coherence/workflow.json +13 -13
- package/skills/engineering/ensure-coverage/SKILL.md +1 -1
- package/skills/engineering/golden-path/SKILL.md +1 -1
- package/skills/engineering/harness-runtime/SKILL.md +1 -1
- package/skills/engineering/resolve-issues/SKILL.md +1 -1
- package/skills/engineering/resolve-issues/references/exploration.md +1 -1
- package/skills/engineering/resolve-issues/references/review.md +27 -4
- package/skills/engineering/resolve-release/SKILL.md +1 -1
- package/skills/engineering/rush-issues/SKILL.md +1 -1
- package/skills/engineering/rush-release/SKILL.md +1 -1
- package/skills/engineering/test-quality/SKILL.md +3 -1
- package/skills/engineering/test-quality/scripts/persist-conventions-core.mjs +231 -21
- package/skills/engineering/test-quality/scripts/persist-conventions.mjs +13 -1
- package/skills/risk-compliance/compliance-audit/SKILL.md +1 -1
- package/skills/risk-compliance/contract-compare/SKILL.md +1 -1
- package/skills/risk-compliance/contract-review/SKILL.md +1 -1
- package/skills/risk-compliance/doc-authenticity/SKILL.md +1 -1
- package/skills/risk-compliance/fraud-detection/SKILL.md +1 -1
- package/skills/risk-compliance/payment-analysis/SKILL.md +1 -1
- package/skills/risk-compliance/portfolio-payment-analysis/SKILL.md +1 -1
- package/skills/risk-compliance/similar-domain/SKILL.md +1 -1
- package/skills/risk-compliance/tld-detection/SKILL.md +1 -1
- package/skills/app-dev/android-foundation/SKILL.md +0 -248
- package/skills/app-dev/android-foundation/references/delivery-focus.md +0 -31
- package/skills/app-dev/android-foundation/references/feature-work.md +0 -50
- package/skills/app-dev/android-foundation/references/setup.md +0 -188
- package/skills/app-dev/android-foundation/references/workspace.md +0 -81
- /package/skills/app-dev/{android-foundation → android-development}/LICENSE +0 -0
- /package/skills/app-dev/{android-foundation → android-development}/assets/android-ci-job.yml +0 -0
- /package/skills/app-dev/{android-foundation → android-development}/release-check.mjs +0 -0
- /package/skills/app-dev/{android-foundation → android-development}/scripts/device-run.sh +0 -0
- /package/skills/app-dev/{android-foundation → android-development}/scripts/workspace-runtime/bin/workspaces.mjs +0 -0
- /package/skills/app-dev/{android-foundation → android-development}/scripts/workspace-runtime/native-workspace.mjs +0 -0
- /package/skills/app-dev/{android-foundation → android-development}/scripts/workspace-runtime/workspace-lifecycle.mjs +0 -0
|
@@ -13,6 +13,12 @@
|
|
|
13
13
|
// A section sits between `conventions:<id>` markers, so a re-run replaces only
|
|
14
14
|
// that block and keeps everything else, including other skills' sections.
|
|
15
15
|
// Nothing is written without --write: the user triggers the write, not an agent.
|
|
16
|
+
//
|
|
17
|
+
// The guide pointer and a skill's task-entry loader are independent. A link to
|
|
18
|
+
// CONTRIBUTING.md says where the policy lives; it does not load the skill that applies
|
|
19
|
+
// it. So a repo whose AGENTS.md (or regular CLAUDE.md) already names the guide still
|
|
20
|
+
// gets the loader, and a repo that already loads the skill keeps its own rule.
|
|
21
|
+
// engineering's init.mjs reuses `withTaskEntries` for its own two loaders.
|
|
16
22
|
|
|
17
23
|
import fs from 'node:fs'
|
|
18
24
|
import path from 'node:path'
|
|
@@ -20,6 +26,8 @@ import path from 'node:path'
|
|
|
20
26
|
export const CONTRIBUTING_FILE = 'CONTRIBUTING.md'
|
|
21
27
|
const MENTIONS_CONTRIBUTING = /(?:^|[^\w-])CONTRIBUTING\.md/
|
|
22
28
|
|
|
29
|
+
const TASK_ENTRY = 'task-entry'
|
|
30
|
+
|
|
23
31
|
export const AGENTS_SECTION = [
|
|
24
32
|
'## Contributing',
|
|
25
33
|
'',
|
|
@@ -69,26 +77,218 @@ export function renderClaude(current) {
|
|
|
69
77
|
return `${current.replace(/\s*$/, '')}\n\n${AGENTS_SECTION}`
|
|
70
78
|
}
|
|
71
79
|
|
|
72
|
-
export function
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
80
|
+
export function taskEntryMarkers(id) {
|
|
81
|
+
return [`<!-- conventions:${id}-${TASK_ENTRY}:start -->`, `<!-- conventions:${id}-${TASK_ENTRY}:end -->`]
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Pure: the managed loader block. A short rule in an entrypoint; the conventions it
|
|
85
|
+
// points at stay in CONTRIBUTING.md.
|
|
86
|
+
export function taskEntryBlock({ id, title, body }) {
|
|
87
|
+
const [start, end] = taskEntryMarkers(id)
|
|
88
|
+
const recovery = body.includes('After context loss') ? '' : '\nAfter context loss, reload this skill\'s rules before continuing.'
|
|
89
|
+
return `${start}\n## ${title}\n\n${body.trim()}${recovery}\n${end}\n`
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const escape = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
93
|
+
function withoutExamples(text) {
|
|
94
|
+
let fence = null
|
|
95
|
+
let comment = false
|
|
96
|
+
const active = text.split('\n').map(line => {
|
|
97
|
+
if (fence) {
|
|
98
|
+
const match = /^ {0,3}(`{3,}|~{3,})/.exec(line)
|
|
99
|
+
if (match && match[1][0] === fence[0] && match[1].length >= fence.length
|
|
100
|
+
&& line.slice(match[0].length).trim() === '') fence = null
|
|
101
|
+
return ''
|
|
102
|
+
}
|
|
103
|
+
let masked = ''
|
|
104
|
+
let cursor = 0
|
|
105
|
+
while (cursor < line.length) {
|
|
106
|
+
const marker = line.indexOf(comment ? '-->' : '<!--', cursor)
|
|
107
|
+
const end = marker < 0 ? line.length : marker
|
|
108
|
+
masked += comment ? ' '.repeat(end - cursor) : line.slice(cursor, end)
|
|
109
|
+
if (marker < 0) break
|
|
110
|
+
const length = comment ? 3 : 4
|
|
111
|
+
masked += ' '.repeat(length)
|
|
112
|
+
cursor = marker + length
|
|
113
|
+
comment = !comment
|
|
114
|
+
}
|
|
115
|
+
const match = /^ {0,3}(`{3,}|~{3,})/.exec(masked)
|
|
116
|
+
if (match) { fence = match[1]; return '' }
|
|
117
|
+
return /^(?: {4}|\t|\s*>)/.test(line) ? '' : line
|
|
118
|
+
}).join('\n')
|
|
119
|
+
if (fence) throw new Error('unclosed Markdown code fence; repair guidance before setup')
|
|
120
|
+
if (comment) {
|
|
121
|
+
throw new Error('unclosed HTML comment; repair guidance before setup')
|
|
122
|
+
}
|
|
123
|
+
return active
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Keep code-form skill names, but not inline examples containing whole instructions.
|
|
127
|
+
const visible = (text) => withoutExamples(text).replace(/<!--[\s\S]*?-->/g, '')
|
|
128
|
+
.replace(/(`+)([^`]*?)\1/g, (span, _ticks, content) => /\s/.test(content) ? '' : span)
|
|
129
|
+
|
|
130
|
+
function hasHeading(text, title) {
|
|
131
|
+
return new RegExp(`^#{1,6}\\s+${escape(title)}\\s*$`, 'm').test(visible(text))
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// A hand-written equivalent ("read and apply the `test-quality` skill") already loads
|
|
135
|
+
// the skill, so the managed block would repeat it. A guide link never matches.
|
|
136
|
+
function loadsSkill(text, skill, contexts = []) {
|
|
137
|
+
const name = escape(skill)
|
|
138
|
+
const instruction = new RegExp(`\\b(?:read|load|apply|use|follow)\\s+(?:and\\s+(?:apply|read)\\s+)?(?:(?:the|installed)\\s+){0,2}(?:\`${name}\`|${name}\\s+skills?\\b)`, 'i')
|
|
139
|
+
return visible(text).split(/\n\s*\n/).some(paragraph =>
|
|
140
|
+
instruction.test(paragraph)
|
|
141
|
+
&& (!contexts.length || contexts.every(word => new RegExp(`\\b${escape(word)}\\b`, 'i').test(paragraph))))
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Pure: `{ text, conflicts }` — each loader's block added once, where it is missing.
|
|
145
|
+
// An equivalent existing rule is preserved; malformed markers and an unmanaged section
|
|
146
|
+
// under the same heading are reported instead of written over.
|
|
147
|
+
export function withTaskEntries(current, loaders = []) {
|
|
148
|
+
let text = current
|
|
149
|
+
const conflicts = []
|
|
150
|
+
for (const loader of loaders) {
|
|
151
|
+
const body = visible(text)
|
|
152
|
+
const denied = new RegExp(`\\b(?:do not|don't|never|must not)\\s+(?:(?:automatically|ever)\\s+)?(?:read|load|apply|use|follow)\\b[^.\\n]{0,120}\\b${escape(loader.skill)}\\b`, 'i')
|
|
153
|
+
if (denied.test(body)) {
|
|
154
|
+
conflicts.push(`an existing rule forbids loading "${loader.skill}"; reconcile it before setup`)
|
|
155
|
+
continue
|
|
156
|
+
}
|
|
157
|
+
const restricted = new RegExp(`\\b${escape(loader.skill)}\\b[^.\\n]{0,100}\\bonly\\s+(?:when|if)\\s+(?:explicitly|the user)`, 'i')
|
|
158
|
+
if (restricted.test(body)) {
|
|
159
|
+
conflicts.push(`an existing explicit-only rule restricts "${loader.skill}"; reconcile it before setup`)
|
|
160
|
+
continue
|
|
161
|
+
}
|
|
162
|
+
if (loader.contexts?.length && visible(text).split(/\n\s*\n/).some(paragraph =>
|
|
163
|
+
loadsSkill(paragraph, loader.skill) && /\b(?:for|during)\b[^.\n]{0,80}\bonly\s*[,;:]/i.test(paragraph))) {
|
|
164
|
+
conflicts.push(`an existing task-only rule restricts "${loader.skill}"; reconcile it before setup`)
|
|
165
|
+
continue
|
|
166
|
+
}
|
|
167
|
+
const [start, end] = taskEntryMarkers(loader.id)
|
|
168
|
+
const active = withoutExamples(text)
|
|
169
|
+
const starts = active.split(start).length - 1
|
|
170
|
+
const ends = active.split(end).length - 1
|
|
171
|
+
if (starts || ends) {
|
|
172
|
+
// Already written: a re-run leaves the block as it is rather than refreshing it.
|
|
173
|
+
if (starts !== 1 || ends !== 1 || active.indexOf(start) > active.indexOf(end)) {
|
|
174
|
+
conflicts.push(`malformed "${loader.title}" markers; repair them by hand`)
|
|
175
|
+
} else if (!loadsSkill(active.slice(active.indexOf(start), active.indexOf(end)), loader.skill, loader.contexts)) {
|
|
176
|
+
conflicts.push(`the preserved "${loader.title}" block has no applicable skill loader; reconcile it before setup`)
|
|
177
|
+
}
|
|
178
|
+
continue
|
|
179
|
+
}
|
|
180
|
+
if (loadsSkill(text, loader.skill, loader.contexts)) continue
|
|
181
|
+
if (hasHeading(text, loader.title)) {
|
|
182
|
+
conflicts.push(`an existing "${loader.title}" section needs manual reconciliation; no competing block added`)
|
|
183
|
+
continue
|
|
184
|
+
}
|
|
185
|
+
text = `${text.replace(/\s*$/, '')}\n\n${taskEntryBlock(loader)}`
|
|
186
|
+
}
|
|
187
|
+
return { text, conflicts }
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function indexEntries(section) {
|
|
191
|
+
const loaders = section.loader ? [section.loader] : []
|
|
192
|
+
return [
|
|
193
|
+
{ name: 'AGENTS.md', render: renderAgents, loaders },
|
|
194
|
+
// A regular CLAUDE.md that already points at AGENTS.md inherits that file's loaders.
|
|
195
|
+
{ name: 'CLAUDE.md', render: renderClaude, loaders, reachesAgents: true },
|
|
78
196
|
]
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function reachesSelectedAgents(text, repoRoot, file) {
|
|
200
|
+
const body = visible(text)
|
|
201
|
+
const expected = path.join(repoRoot, 'AGENTS.md')
|
|
202
|
+
const canonical = name => fs.existsSync(name) ? fs.realpathSync(name) : path.resolve(name)
|
|
203
|
+
const affirmative = body.split('\n').filter(line => !/\b(?:do not|don't|never|must not)\b/i.test(line)).join('\n')
|
|
204
|
+
const references = [...affirmative.matchAll(/\[[^\]]*\]\((?:<([^>]+)>|([^\s)]+))\)/g)]
|
|
205
|
+
.map(match => match[1] ?? match[2])
|
|
206
|
+
for (const match of affirmative.matchAll(/\b(?:read|load|follow|see|consult)\s+`?((?:[^\s`]+\/)?AGENTS\.md)`?/gi)) {
|
|
207
|
+
references.push(match[1])
|
|
208
|
+
}
|
|
209
|
+
return references.some(ref => {
|
|
210
|
+
if (/^[a-z][a-z\d+.-]*:|^\/\//i.test(ref)) return false
|
|
211
|
+
try {
|
|
212
|
+
const target = path.resolve(path.dirname(file), decodeURIComponent(ref.split(/[?#]/)[0]))
|
|
213
|
+
return canonical(target) === canonical(expected)
|
|
214
|
+
} catch { return false }
|
|
215
|
+
})
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function apply(repoRoot, entries, write) {
|
|
219
|
+
repoRoot = fs.realpathSync(repoRoot)
|
|
220
|
+
const readText = file => new TextDecoder('utf-8', { fatal: true, ignoreBOM: true }).decode(fs.readFileSync(file))
|
|
221
|
+
const plannedContent = new Map()
|
|
222
|
+
const plan = entries.map((entry) => {
|
|
223
|
+
const file = path.join(repoRoot, entry.name)
|
|
224
|
+
if (fs.lstatSync(file, { throwIfNoEntry: false })?.isSymbolicLink()) {
|
|
225
|
+
let conflicts = []
|
|
226
|
+
try {
|
|
227
|
+
const target = fs.realpathSync(file)
|
|
228
|
+
const relative = path.relative(repoRoot, target)
|
|
229
|
+
if (relative === '..' || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {
|
|
230
|
+
conflicts = ['linked entrypoint is outside the selected project; verify its task loader separately']
|
|
231
|
+
} else {
|
|
232
|
+
const current = plannedContent.get(target) ?? readText(file)
|
|
233
|
+
const checked = withTaskEntries(current, entry.loaders)
|
|
234
|
+
conflicts = checked.conflicts
|
|
235
|
+
if (checked.text !== current && !(entry.reachesAgents && reachesSelectedAgents(current, repoRoot, target))) {
|
|
236
|
+
conflicts.push('linked entrypoint lacks the task loader; explicit reconciliation is required')
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
} catch { conflicts = ['linked entrypoint could not be verified; setup is partial'] }
|
|
240
|
+
return { file: entry.name, action: 'unchanged', conflicts }
|
|
241
|
+
}
|
|
242
|
+
const current = fs.existsSync(file) ? readText(file) : null
|
|
243
|
+
const base = entry.render(current)
|
|
244
|
+
let next = base
|
|
245
|
+
let conflicts = []
|
|
246
|
+
if (base !== LINK_TO_AGENTS) {
|
|
247
|
+
const applied = withTaskEntries(base, entry.loaders)
|
|
248
|
+
const inherits = entry.reachesAgents && reachesSelectedAgents(current ?? '', repoRoot, file)
|
|
249
|
+
next = inherits ? base : applied.text
|
|
250
|
+
conflicts = applied.conflicts
|
|
251
|
+
}
|
|
85
252
|
if (next === LINK_TO_AGENTS) {
|
|
86
|
-
|
|
87
|
-
|
|
253
|
+
return { file: entry.name, action: 'link', conflicts, next }
|
|
254
|
+
}
|
|
255
|
+
const action = current == null ? 'create' : next === current ? 'unchanged' : 'update'
|
|
256
|
+
if (current !== null && action !== 'unchanged' && fs.statSync(file).nlink > 1) {
|
|
257
|
+
conflicts.push('hard-linked guidance cannot be changed without exclusive ownership')
|
|
88
258
|
}
|
|
89
|
-
|
|
90
|
-
return { file: name, action }
|
|
259
|
+
plannedContent.set(file, next)
|
|
260
|
+
return { file: entry.name, action, conflicts, next }
|
|
91
261
|
})
|
|
262
|
+
const blocked = plan.some(entry => entry.conflicts.length)
|
|
263
|
+
if (write && !blocked) {
|
|
264
|
+
for (const entry of plan.filter(entry => entry.action !== 'unchanged')) {
|
|
265
|
+
const file = path.join(repoRoot, entry.file)
|
|
266
|
+
fs.accessSync(entry.action === 'create' || entry.action === 'link' ? repoRoot : file, fs.constants.W_OK)
|
|
267
|
+
}
|
|
268
|
+
for (const entry of plan) {
|
|
269
|
+
const file = path.join(repoRoot, entry.file)
|
|
270
|
+
if (entry.next === LINK_TO_AGENTS) fs.symlinkSync('AGENTS.md', file)
|
|
271
|
+
else if (entry.action !== 'unchanged') fs.writeFileSync(file, entry.next)
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
return plan.map(({ next: _next, ...entry }) => ({
|
|
275
|
+
...entry, action: blocked && entry.action !== 'unchanged' ? 'blocked' : entry.action,
|
|
276
|
+
}))
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export function persist(repoRoot, section, { write = false } = {}) {
|
|
280
|
+
const block = sectionBlock(section)
|
|
281
|
+
const entries = [
|
|
282
|
+
{ name: CONTRIBUTING_FILE, render: (text) => renderConventions(text, section.id, block), loaders: [] },
|
|
283
|
+
...indexEntries(section),
|
|
284
|
+
]
|
|
285
|
+
return apply(repoRoot, entries, write)
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// The index half alone: for a section that is already present, only the guide pointer
|
|
289
|
+
// and the task-entry loader are due.
|
|
290
|
+
export function persistIndex(repoRoot, section, { write = false } = {}) {
|
|
291
|
+
return apply(repoRoot, indexEntries(section), write)
|
|
92
292
|
}
|
|
93
293
|
|
|
94
294
|
// `section` = { id, owner, title, body }. Exit 0 on success, 2 on a usage error.
|
|
@@ -107,12 +307,22 @@ export function runCli(argv, section, { cwd = process.cwd(), log = console.log }
|
|
|
107
307
|
log(`persist-conventions: not a directory: ${repoRoot}`)
|
|
108
308
|
return 2
|
|
109
309
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
const
|
|
310
|
+
let results
|
|
311
|
+
try { results = persist(repoRoot, section, { write }) }
|
|
312
|
+
catch (error) { log(`persist-conventions: setup refused: ${error.message}`); return 2 }
|
|
313
|
+
const done = { create: 'created', link: 'linked to AGENTS.md', update: 'updated', unchanged: 'unchanged', blocked: 'blocked' }
|
|
314
|
+
const conflicts = []
|
|
315
|
+
for (const r of results) {
|
|
316
|
+
for (const conflict of r.conflicts ?? []) conflicts.push(`${r.file}: ${conflict}`)
|
|
317
|
+
log(` ${r.file}: ${write || r.action === 'unchanged' ? done[r.action] : `would ${r.action}`}`)
|
|
318
|
+
}
|
|
319
|
+
const changed = results.filter((r) => !['unchanged', 'blocked'].includes(r.action)).length
|
|
114
320
|
log(write
|
|
115
321
|
? `persist-conventions: ${section.title}: ${changed} file(s) written, ${results.length - changed} unchanged`
|
|
116
322
|
: `persist-conventions: ${section.title}: preview only, ${changed} file(s) would change; re-run with --write to write`)
|
|
117
|
-
|
|
323
|
+
if (conflicts.length) {
|
|
324
|
+
for (const conflict of conflicts) log(` conflict: ${conflict}`)
|
|
325
|
+
log(`persist-conventions: ${section.title}: ${conflicts.length} conflict(s); no files written; reconcile before setup`)
|
|
326
|
+
}
|
|
327
|
+
return conflicts.length ? 2 : 0
|
|
118
328
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// persist-conventions — the test principles as the `## Testing conventions`
|
|
2
2
|
// section of a repo's CONTRIBUTING.md (../SKILL.md, Persist mode). Run only when
|
|
3
|
-
// the user asks; previews unless --write.
|
|
3
|
+
// the user asks; previews unless --write. --write also adds the skill's task-entry
|
|
4
|
+
// loader to AGENTS.md (and to a regular CLAUDE.md that does not reach AGENTS.md).
|
|
4
5
|
//
|
|
5
6
|
// node persist-conventions.mjs [--repo-root <dir>] [--write]
|
|
6
7
|
import fs from 'node:fs'
|
|
@@ -12,6 +13,17 @@ const SECTION = {
|
|
|
12
13
|
owner: 'test-quality',
|
|
13
14
|
title: 'Testing conventions',
|
|
14
15
|
body: fs.readFileSync(fileURLToPath(new URL('../references/testing-conventions.md', import.meta.url)), 'utf8'),
|
|
16
|
+
loader: {
|
|
17
|
+
id: 'testing',
|
|
18
|
+
skill: 'test-quality',
|
|
19
|
+
title: 'Testing task entry',
|
|
20
|
+
body: [
|
|
21
|
+
'When writing, changing, or reviewing tests, read and apply the installed',
|
|
22
|
+
'`test-quality` skill before choosing test shape or adding a gate. The',
|
|
23
|
+
"repository's testing rules stay in its contributor guide; loading the skill",
|
|
24
|
+
'adds no new requirement.',
|
|
25
|
+
].join('\n'),
|
|
26
|
+
},
|
|
15
27
|
}
|
|
16
28
|
|
|
17
29
|
process.exitCode = runCli(process.argv.slice(2), SECTION)
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: compliance-audit
|
|
3
3
|
version: 2.9.0
|
|
4
4
|
description: "Audit operations against financial-compliance checklists (AML/KYC, VARS, PCI-DSS, TPA oversight, onboarding, transaction monitoring). Produces tracker.md (with a tracker.xlsx export) and phase-trail.md with layered validation; cites evidence by original filename. Input: reg-parser or the built-in VARS checklist."
|
|
5
|
-
release-version: 0.
|
|
5
|
+
release-version: 0.47.0
|
|
6
6
|
---
|
|
7
7
|
**Updates:** Once/session run `node <skill-base-dir>/release-check.mjs`.
|
|
8
8
|
# Compliance Audit
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: contract-compare
|
|
3
3
|
description: "Compare two or more contracts side-by-side for structured diff, gap analysis, and benchmarking. Use when: comparing contracts or agreements, contract diff, compare terms, vendor-vs-vendor, compare our template vs theirs, compliance gap between contracts. NOT for single-contract risk review — use contract-review for that."
|
|
4
|
-
release-version: 0.
|
|
4
|
+
release-version: 0.47.0
|
|
5
5
|
---
|
|
6
6
|
**Updates:** Once/session run `node <skill-base-dir>/release-check.mjs`.
|
|
7
7
|
# Contract Compare
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: contract-review
|
|
3
3
|
description: "Review contracts for legal, commercial, and compliance risks via convert → chunk review → merge → validate (+ optional mandate preprocessing). Use for reviewing, auditing, or checking any contract, agreement, NDA, MSA, or SLA; input a file path (PDF/DOCX/TXT) or pasted text."
|
|
4
|
-
release-version: 0.
|
|
4
|
+
release-version: 0.47.0
|
|
5
5
|
---
|
|
6
6
|
**Updates:** Once/session run `node <skill-base-dir>/release-check.mjs`.
|
|
7
7
|
# Contract Review
|
|
@@ -3,7 +3,7 @@ name: doc-authenticity
|
|
|
3
3
|
version: 0.4.1
|
|
4
4
|
description: "Screen PDF documents and raster images for fabrication, tampering, Photoshop or other image-editor use, and content validity. Detects file type, runs format-specific authenticity forensics, classifies bank statements, conversation logs, invoices, agreements, and other documents, then applies type-specific validity checks. Produces separate authenticity (NO_EVIDENCE_OF_MODIFICATION / SUSPICIOUS / LIKELY_MODIFIED / INCONCLUSIVE) and validity (VALID / INVALID / INCONCLUSIVE) verdicts. Use whenever a user asks whether a document, screenshot, scan, statement, invoice, contract, chat log, receipt, or payment evidence is genuine, original, edited, Photoshopped, manipulated, fake, or valid."
|
|
5
5
|
disable-model-invocation: true
|
|
6
|
-
release-version: 0.
|
|
6
|
+
release-version: 0.47.0
|
|
7
7
|
---
|
|
8
8
|
**Updates:** Once/session run `node <skill-base-dir>/release-check.mjs`.
|
|
9
9
|
# Document and Image Authenticity
|
|
@@ -12,7 +12,7 @@ description: >
|
|
|
12
12
|
impact, and a business report. Use for fraud, scam, mule, account-takeover,
|
|
13
13
|
chargeback, dispute, transfer, collection, or payout risk analysis even when the
|
|
14
14
|
user does not name this skill.
|
|
15
|
-
release-version: 0.
|
|
15
|
+
release-version: 0.47.0
|
|
16
16
|
---
|
|
17
17
|
**Updates:** Once/session run `node <skill-base-dir>/release-check.mjs`.
|
|
18
18
|
# Fraud Detection
|
|
@@ -14,7 +14,7 @@ description: >
|
|
|
14
14
|
transactions" — even if they do not say payment-analysis. Do NOT use for
|
|
15
15
|
fraud rule discovery (fraud-detection) or TLD / MCC-miscoding
|
|
16
16
|
clusters (tld-detection).
|
|
17
|
-
release-version: 0.
|
|
17
|
+
release-version: 0.47.0
|
|
18
18
|
---
|
|
19
19
|
**Updates:** Once/session run `node <skill-base-dir>/release-check.mjs`.
|
|
20
20
|
# Payment Analysis
|
|
@@ -5,7 +5,7 @@ description: >
|
|
|
5
5
|
and investigate selected merchants' authorization, settlement and disputes,
|
|
6
6
|
including descriptor mapping and overlapping exports. Portfolio-level payment
|
|
7
7
|
overviews belong to payment-analysis; fraud rule discovery belongs to fraud-detection.
|
|
8
|
-
release-version: 0.
|
|
8
|
+
release-version: 0.47.0
|
|
9
9
|
---
|
|
10
10
|
**Updates:** Once/session run `node <skill-base-dir>/release-check.mjs`.
|
|
11
11
|
# Portfolio payment analysis
|
|
@@ -8,7 +8,7 @@ description: >
|
|
|
8
8
|
links, an entity chain, and a watchlist for the operator's next domains.
|
|
9
9
|
Not transaction data (tld-detection) or fraud rules (fraud-detection).
|
|
10
10
|
disable-model-invocation: true
|
|
11
|
-
release-version: 0.
|
|
11
|
+
release-version: 0.47.0
|
|
12
12
|
---
|
|
13
13
|
**Updates:** Once/session run `node <skill-base-dir>/release-check.mjs`.
|
|
14
14
|
# Similar Domain
|
|
@@ -6,7 +6,7 @@ description: >
|
|
|
6
6
|
patterns, with USD and BIN-country currency views, group profiles and priorities.
|
|
7
7
|
Not payment-analysis, fraud-detection, or website research.
|
|
8
8
|
disable-model-invocation: true
|
|
9
|
-
release-version: 0.
|
|
9
|
+
release-version: 0.47.0
|
|
10
10
|
---
|
|
11
11
|
**Updates:** Once/session run `node <skill-base-dir>/release-check.mjs`.
|
|
12
12
|
# TLD detection
|