dsh-doublecheck 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/LICENSE +201 -0
  3. package/README.es.md +252 -0
  4. package/README.hi.md +252 -0
  5. package/README.md +268 -0
  6. package/README.pt.md +252 -0
  7. package/README.zh.md +266 -0
  8. package/cordis.patch.yml +19 -0
  9. package/lib/domain/evidence.d.ts +100 -0
  10. package/lib/domain/evidence.js +180 -0
  11. package/lib/domain/projection.d.ts +60 -0
  12. package/lib/domain/projection.js +132 -0
  13. package/lib/domain/report.d.ts +93 -0
  14. package/lib/domain/report.js +266 -0
  15. package/lib/domain/stages.d.ts +98 -0
  16. package/lib/domain/stages.js +192 -0
  17. package/lib/domain/vagueness.d.ts +25 -0
  18. package/lib/domain/vagueness.js +56 -0
  19. package/lib/domain/vocabulary.d.ts +50 -0
  20. package/lib/domain/vocabulary.js +16 -0
  21. package/lib/events.d.ts +155 -0
  22. package/lib/events.js +15 -0
  23. package/lib/grill/index.d.ts +49 -0
  24. package/lib/grill/index.js +495 -0
  25. package/lib/grill/provider.d.ts +56 -0
  26. package/lib/grill/provider.js +189 -0
  27. package/lib/guard/command.d.ts +47 -0
  28. package/lib/guard/command.js +132 -0
  29. package/lib/guard/index.d.ts +111 -0
  30. package/lib/guard/index.js +516 -0
  31. package/lib/guard/prose.d.ts +34 -0
  32. package/lib/guard/prose.js +80 -0
  33. package/lib/guard/review.d.ts +67 -0
  34. package/lib/guard/review.js +184 -0
  35. package/lib/index.d.ts +17 -0
  36. package/lib/index.js +15 -0
  37. package/lib/invariant.d.ts +56 -0
  38. package/lib/invariant.js +112 -0
  39. package/lib/types.d.ts +39 -0
  40. package/lib/types.js +18 -0
  41. package/package.json +109 -0
  42. package/skills/delivery-proof/SKILL.md +52 -0
  43. package/skills/delivery-review/SKILL.md +49 -0
  44. package/skills/grill-requirements/SKILL.md +98 -0
  45. package/skills/red-green-tdd/SKILL.md +57 -0
@@ -0,0 +1,495 @@
1
+ /**
2
+ * The grill module: dsh-doublecheck's requirements-furnace plugin.
3
+ *
4
+ * Registers the bundled `skills/` directory on the DSH skill registry (the
5
+ * official skill capability seam), exposes the model-facing catalog/loader
6
+ * tool `doublecheck_skills`, and provides the two contract tools:
7
+ * `doublecheck_spec` (commit a grilled requirements spec to the session log
8
+ * and a workspace markdown file) and `doublecheck_report` (v0.4: consolidate
9
+ * the session's discipline evidence into a delivery report, optionally
10
+ * orchestrating a per-dimension verification workflow through the DSH
11
+ * workflow seam).
12
+ *
13
+ * @module dsh-doublecheck/grill
14
+ */
15
+ import { renderSkillContent } from '@deepseek-ai/dsh-skill';
16
+ import { defineTool } from '@deepseek-ai/dsh-tools';
17
+ import z from '@deepseek-ai/schemastery';
18
+ import { DEFAULT_MUTATION_TOOLS, DEFAULT_TEST_COMMAND_PATTERNS, DEFAULT_TEST_FILE_PATTERNS, DEFAULT_TEST_TOOL_NAMES, compileDetection, } from "../domain/evidence.js";
19
+ import { SPEC_TOOL_NAME } from "../domain/stages.js";
20
+ import { buildVerifyScript, deriveReportVerdict, foldReportFacts, renderReportMarkdown, VERIFY_META, } from "../domain/report.js";
21
+ import { VERIFY_DIMENSIONS } from "../domain/vocabulary.js";
22
+ import { BundledSkillProvider, PROVIDER_NAME } from "./provider.js";
23
+ export const name = 'doublecheck-grill';
24
+ export const inject = ['skills', 'tools'];
25
+ export const Config = z.object({
26
+ specFile: z.string().min(1).default('doublecheck-spec.md'),
27
+ reportFile: z.string().min(1).default('doublecheck-report.md'),
28
+ reportVerify: z.boolean().default(true),
29
+ verifyProvider: z.string().min(1).default('fork'),
30
+ verifyMode: z.union(['all', 'single']).default('all'),
31
+ reportTestToolNames: z.array(z.string()).default([...DEFAULT_TEST_TOOL_NAMES]),
32
+ reportTestCommandPatterns: z.array(z.string()).default([...DEFAULT_TEST_COMMAND_PATTERNS]),
33
+ reportMutationTools: z.array(z.string()).default([...DEFAULT_MUTATION_TOOLS]),
34
+ reportTestFilePatterns: z.array(z.string()).default([...DEFAULT_TEST_FILE_PATTERNS]),
35
+ });
36
+ /** Model-facing catalog of the package's own skills, one line per skill. */
37
+ function renderCatalog(skills) {
38
+ if (skills.length === 0)
39
+ return 'No doublecheck skills are currently available.';
40
+ return [
41
+ 'The following doublecheck discipline skills are available:',
42
+ ...skills.map(skill => `- \`${skill.name}\`: ${skill.description}`),
43
+ ].join('\n');
44
+ }
45
+ /** The rendered spec document written to the workspace and shown to the model. */
46
+ export function renderSpecMarkdown(spec) {
47
+ return [
48
+ '# Doublecheck spec',
49
+ '',
50
+ '## Goal',
51
+ spec.goal,
52
+ '',
53
+ '## Scope',
54
+ spec.scope,
55
+ '',
56
+ '## Acceptance criteria',
57
+ spec.acceptanceCriteria,
58
+ '',
59
+ '## Failure modes',
60
+ spec.failureModes,
61
+ '',
62
+ '## Priorities',
63
+ spec.priorities,
64
+ '',
65
+ '## Non-goals',
66
+ spec.nonGoals,
67
+ '',
68
+ ].join('\n');
69
+ }
70
+ /**
71
+ * Install the grill module: bundled skill provider plus its three tools.
72
+ * @param ctx - plugin context; registrations unwind with it.
73
+ * @param config - validated {@link Config}.
74
+ */
75
+ export function apply(ctx, config) {
76
+ ctx.skills.registerProvider(() => new BundledSkillProvider(ctx));
77
+ const reportDetection = compileDetection({
78
+ testToolNames: config.reportTestToolNames,
79
+ testCommandPatterns: config.reportTestCommandPatterns,
80
+ guardTools: config.reportMutationTools,
81
+ testFilePatterns: config.reportTestFilePatterns,
82
+ });
83
+ ctx.tools.register(defineTool({
84
+ name: 'doublecheck_skills',
85
+ description: 'List the doublecheck engineering-discipline skills, or load the full instructions of one by its exact name. Call this before acting on a task that names or clearly matches one of them.',
86
+ parameters: {
87
+ name: {
88
+ type: 'string',
89
+ description: 'Exact skill name to load; omit to list every available doublecheck skill.',
90
+ },
91
+ },
92
+ output: {
93
+ schema: {
94
+ type: 'object',
95
+ additionalProperties: false,
96
+ properties: {
97
+ skills: {
98
+ type: 'array',
99
+ required: true,
100
+ items: {
101
+ type: 'object',
102
+ additionalProperties: false,
103
+ properties: {
104
+ name: { type: 'string', required: true },
105
+ description: { type: 'string', required: true },
106
+ whenToUse: { type: 'string' },
107
+ },
108
+ },
109
+ },
110
+ name: { type: 'string' },
111
+ provider: { type: 'string' },
112
+ resourceBase: {
113
+ type: 'object',
114
+ additionalProperties: false,
115
+ properties: {
116
+ kind: { type: 'string', required: true, const: 'directory' },
117
+ path: { type: 'string', required: true },
118
+ },
119
+ },
120
+ content: { type: 'string' },
121
+ },
122
+ },
123
+ render: (_args, value) => {
124
+ if (value.content === undefined || value.provider === undefined) {
125
+ return [{ type: 'text', text: renderCatalog(value.skills) }];
126
+ }
127
+ const name = value.name ?? _args.name ?? '';
128
+ const loaded = renderSkillContent({
129
+ name,
130
+ provider: value.provider,
131
+ ...value.resourceBase !== undefined ? { resourceBase: value.resourceBase } : {},
132
+ content: value.content,
133
+ });
134
+ return [{ type: 'text', text: loaded }];
135
+ },
136
+ },
137
+ async execute(args, exec) {
138
+ const lookup = { cwd: exec.agent?.session.header.cwd, signal: exec.signal, scope: exec.agent };
139
+ const summaries = (await ctx.skills.list(lookup)).filter(skill => skill.provider === PROVIDER_NAME);
140
+ const skills = summaries.map(({ name: skillName, description, whenToUse }) => ({
141
+ name: skillName,
142
+ description,
143
+ ...whenToUse !== undefined ? { whenToUse } : {},
144
+ }));
145
+ if (args.name === undefined)
146
+ return { skills };
147
+ const skill = await ctx.skills.get(args.name, lookup);
148
+ if (skill === undefined || skill.provider !== PROVIDER_NAME) {
149
+ throw new Error(`skill "${args.name}" is not a doublecheck skill or is no longer available`);
150
+ }
151
+ const resourceBase = skill.resourceBase !== undefined && skill.resourceBase.kind === 'directory'
152
+ ? { kind: 'directory', path: skill.resourceBase.path }
153
+ : undefined;
154
+ return {
155
+ skills,
156
+ name: skill.name,
157
+ provider: skill.provider,
158
+ ...resourceBase !== undefined ? { resourceBase } : {},
159
+ content: skill.content,
160
+ };
161
+ },
162
+ presentCall(args) {
163
+ return args.name === undefined
164
+ ? { card: 'generic', title: 'List doublecheck skills', kind: 'read' }
165
+ : { card: 'generic', title: `Load skill ${args.name}`, kind: 'read', rawInput: args.name };
166
+ },
167
+ }));
168
+ ctx.tools.register(defineTool({
169
+ name: SPEC_TOOL_NAME,
170
+ description: 'Record the grilled requirements spec for the current task. Fill all six fields after the requirements grill reaches consensus (goal, scope, acceptance criteria, failure modes, priorities, non-goals), so the contract is kept in the session and written to the workspace before implementation starts.',
171
+ parameters: {
172
+ goal: { type: 'string', required: true, description: 'What outcome the work must produce, in one verifiable sentence.' },
173
+ scope: { type: 'string', required: true, description: 'What is in scope and what is out of scope for this change.' },
174
+ acceptanceCriteria: { type: 'string', required: true, description: 'Observable checks that prove the work is done.' },
175
+ failureModes: { type: 'string', required: true, description: 'What can go wrong and the correct behavior in each case.' },
176
+ priorities: { type: 'string', required: true, description: 'What to trade when goals conflict; what is optional.' },
177
+ nonGoals: { type: 'string', required: true, description: 'What the user explicitly does not want.' },
178
+ filePath: { type: 'string', description: 'Optional workspace path for the markdown copy. Defaults to the configured spec file in the session working directory.' },
179
+ },
180
+ output: {
181
+ schema: {
182
+ type: 'object',
183
+ additionalProperties: false,
184
+ properties: {
185
+ spec: {
186
+ type: 'object',
187
+ required: true,
188
+ additionalProperties: false,
189
+ properties: {
190
+ goal: { type: 'string', required: true },
191
+ scope: { type: 'string', required: true },
192
+ acceptanceCriteria: { type: 'string', required: true },
193
+ failureModes: { type: 'string', required: true },
194
+ priorities: { type: 'string', required: true },
195
+ nonGoals: { type: 'string', required: true },
196
+ },
197
+ },
198
+ path: { oneOf: [{ type: 'string' }, { type: 'null' }], required: true },
199
+ written: { type: 'boolean', required: true },
200
+ },
201
+ },
202
+ render: (_args, value) => {
203
+ const note = value.written
204
+ ? `Recorded to ${value.path}.`
205
+ : 'No workspace copy was written; the spec is recorded in this session only.';
206
+ return [{ type: 'text', text: `${renderSpecMarkdown(value.spec)}\n${note}` }];
207
+ },
208
+ },
209
+ async execute(args, exec) {
210
+ const spec = {
211
+ goal: args.goal,
212
+ scope: args.scope,
213
+ acceptanceCriteria: args.acceptanceCriteria,
214
+ failureModes: args.failureModes,
215
+ priorities: args.priorities,
216
+ nonGoals: args.nonGoals,
217
+ };
218
+ const outcome = await writeSpecFile(ctx, spec, args.filePath ?? config.specFile, exec);
219
+ if (exec.agent !== undefined) {
220
+ ctx.emit('doublecheck/spec', { session: exec.agent.session, spec, ...outcome });
221
+ }
222
+ return { spec, ...outcome };
223
+ },
224
+ presentCall(args) {
225
+ return {
226
+ card: 'generic',
227
+ title: 'Record doublecheck spec',
228
+ kind: 'edit',
229
+ content: [{ type: 'text', text: args.goal.length > 240 ? `${args.goal.slice(0, 240)}…` : args.goal }],
230
+ };
231
+ },
232
+ }));
233
+ ctx.tools.register(defineTool({
234
+ name: 'doublecheck_report',
235
+ description: 'Consolidate this session\'s doublecheck discipline evidence into a delivery report: the committed spec, the red/green test timeline, the adversary review record, and a derived verdict. With verify enabled, orchestrates one parallel checker per spec dimension through the workflow seam and folds their verdicts in. The report is written to the workspace and returned as this call\'s result.',
236
+ parameters: {
237
+ filePath: { type: 'string', description: 'Optional workspace path for the markdown copy. Defaults to the configured report file in the session working directory.' },
238
+ verify: { type: 'boolean', description: 'Whether to run the per-dimension verification workflow. Defaults to the configured reportVerify.' },
239
+ },
240
+ output: {
241
+ schema: {
242
+ type: 'object',
243
+ additionalProperties: false,
244
+ properties: {
245
+ verdict: {
246
+ type: 'string',
247
+ required: true,
248
+ enum: ['grill', 'draft', 'red', 'green', 'objections', 'verified', 'proven', 'challenged', 'unverified'],
249
+ },
250
+ spec: {
251
+ oneOf: [
252
+ {
253
+ type: 'object',
254
+ additionalProperties: false,
255
+ properties: {
256
+ goal: { type: 'string', required: true },
257
+ scope: { type: 'string', required: true },
258
+ acceptanceCriteria: { type: 'string', required: true },
259
+ failureModes: { type: 'string', required: true },
260
+ priorities: { type: 'string', required: true },
261
+ nonGoals: { type: 'string', required: true },
262
+ },
263
+ },
264
+ { type: 'null' },
265
+ ],
266
+ required: true,
267
+ },
268
+ testRuns: {
269
+ type: 'object',
270
+ required: true,
271
+ additionalProperties: false,
272
+ properties: {
273
+ failed: { type: 'integer', required: true },
274
+ passed: { type: 'integer', required: true },
275
+ },
276
+ },
277
+ timeline: {
278
+ type: 'array',
279
+ required: true,
280
+ items: {
281
+ type: 'object',
282
+ additionalProperties: false,
283
+ properties: {
284
+ kind: { type: 'string', required: true, enum: ['spec', 'red', 'green', 'review'] },
285
+ detail: { type: 'string', required: true },
286
+ },
287
+ },
288
+ },
289
+ edits: { type: 'integer', required: true },
290
+ review: {
291
+ oneOf: [
292
+ {
293
+ type: 'object',
294
+ additionalProperties: false,
295
+ properties: {
296
+ verdict: { type: 'string', required: true, enum: ['findings', 'clean', 'unavailable'] },
297
+ findings: {
298
+ type: 'array',
299
+ required: true,
300
+ items: {
301
+ type: 'object',
302
+ additionalProperties: false,
303
+ properties: {
304
+ severity: { type: 'string', required: true, enum: ['blocker', 'major', 'minor', 'info'] },
305
+ title: { type: 'string', required: true },
306
+ detail: { type: 'string', required: true },
307
+ },
308
+ },
309
+ },
310
+ },
311
+ },
312
+ { type: 'null' },
313
+ ],
314
+ required: true,
315
+ },
316
+ verification: {
317
+ oneOf: [
318
+ {
319
+ type: 'object',
320
+ additionalProperties: false,
321
+ properties: {
322
+ checks: {
323
+ type: 'array',
324
+ required: true,
325
+ items: {
326
+ type: 'object',
327
+ additionalProperties: false,
328
+ properties: {
329
+ dimension: { type: 'string', required: true, enum: [...VERIFY_DIMENSIONS] },
330
+ verdict: { type: 'string', required: true, enum: ['pass', 'fail'] },
331
+ evidence: { type: 'string', required: true },
332
+ note: { type: 'string', required: true },
333
+ },
334
+ },
335
+ },
336
+ complete: { type: 'boolean', required: true },
337
+ },
338
+ },
339
+ { type: 'null' },
340
+ ],
341
+ required: true,
342
+ },
343
+ path: { oneOf: [{ type: 'string' }, { type: 'null' }], required: true },
344
+ written: { type: 'boolean', required: true },
345
+ },
346
+ },
347
+ render: (_args, value) => {
348
+ const note = value.written
349
+ ? `Recorded to ${value.path}.`
350
+ : 'No workspace copy was written; the report is returned in this result only.';
351
+ return [{ type: 'text', text: `${renderReportMarkdown(value)}\n${note}` }];
352
+ },
353
+ },
354
+ async execute(args, exec) {
355
+ const session = exec.agent?.session;
356
+ const facts = session === undefined
357
+ ? { spec: null, testRuns: { failed: 0, passed: 0 }, timeline: [], edits: 0, review: null }
358
+ : foldReportFacts(session.events, reportDetection);
359
+ const verify = args.verify ?? config.reportVerify;
360
+ const verification = verify
361
+ ? await runVerifyWorkflow(ctx, config, facts.spec, exec)
362
+ : null;
363
+ const report = {
364
+ ...facts,
365
+ verdict: deriveReportVerdict(facts, verification),
366
+ verification,
367
+ path: null,
368
+ written: false,
369
+ };
370
+ const outcome = await writeReportFile(ctx, report, args.filePath ?? config.reportFile, exec);
371
+ report.path = outcome.path;
372
+ report.written = outcome.written;
373
+ if (session !== undefined) {
374
+ ctx.emit('doublecheck/report', { session, verdict: report.verdict, verification, path: report.path, written: report.written });
375
+ }
376
+ return report;
377
+ },
378
+ presentCall(args) {
379
+ return {
380
+ card: 'generic',
381
+ title: 'Assemble doublecheck report',
382
+ kind: 'read',
383
+ content: [{ type: 'text', text: args.verify === true ? 'verifying delivery' : 'folding session evidence' }],
384
+ };
385
+ },
386
+ }));
387
+ }
388
+ /**
389
+ * Run the per-dimension verification workflow through the workflow seam,
390
+ * when one is mounted. A missing seam or a non-completed run settles as
391
+ * `null` (no verification) — the report states it did not run.
392
+ * @returns the settled checks with the completeness flag, or null when
393
+ * verification did not run at all.
394
+ */
395
+ async function runVerifyWorkflow(ctx, config, spec, exec) {
396
+ if (spec === null || exec.agent === undefined)
397
+ return null;
398
+ const engine = ctx.get('workflowEngine');
399
+ if (engine === undefined) {
400
+ ctx.logger.warn('dsh-doublecheck: verification skipped: the workflowEngine seam is not mounted');
401
+ return null;
402
+ }
403
+ let run;
404
+ try {
405
+ run = engine.start({
406
+ script: buildVerifyScript(config.verifyMode),
407
+ meta: VERIFY_META,
408
+ args: { spec, dimensions: [...VERIFY_DIMENSIONS] },
409
+ subagentProvider: config.verifyProvider,
410
+ maxTotalAgents: config.verifyMode === 'single' ? 1 : VERIFY_DIMENSIONS.length,
411
+ parent: exec.agent,
412
+ signal: exec.signal,
413
+ });
414
+ }
415
+ catch (error) {
416
+ // Invalid script/meta or an absent verify provider: verification did not
417
+ // run, and the report states so instead of failing the report itself.
418
+ ctx.logger.warn(`dsh-doublecheck: verification workflow rejected: ${String(error)}`);
419
+ return null;
420
+ }
421
+ try {
422
+ const result = await run.result;
423
+ if (result.stopReason !== 'completed')
424
+ return null;
425
+ const value = result.value;
426
+ if (value === null || value === undefined || !Array.isArray(value.checks))
427
+ return null;
428
+ const checks = [];
429
+ for (const check of value.checks) {
430
+ if (!VERIFY_DIMENSIONS.includes(check.dimension))
431
+ continue;
432
+ checks.push(check);
433
+ }
434
+ // `proven` requires a verdict for every spec dimension: completeness is
435
+ // judged here so a checker that silently dropped a dimension cannot
436
+ // upgrade the delivery.
437
+ const complete = VERIFY_DIMENSIONS.every(dimension => checks.some(check => check.dimension === dimension));
438
+ return { checks, complete };
439
+ }
440
+ catch (error) {
441
+ // A broken workflow engine must not fail the report call itself: the
442
+ // delivery settles as unverified and the markdown says verification did
443
+ // not run.
444
+ ctx.logger.warn(`dsh-doublecheck: verification workflow failed: ${String(error)}`);
445
+ return null;
446
+ }
447
+ finally {
448
+ await run.dispose();
449
+ }
450
+ }
451
+ /** Write the report markdown through the filesystem seam, when one exists. */
452
+ async function writeReportFile(ctx, report, filePath, exec) {
453
+ const fs = ctx.get('fs');
454
+ if (fs === undefined)
455
+ return { path: null, written: false };
456
+ let target;
457
+ try {
458
+ target = await fs.resolve(filePath, {
459
+ ...exec.agent?.session.header.cwd !== undefined ? { cwd: exec.agent.session.header.cwd } : {},
460
+ signal: exec.signal,
461
+ });
462
+ await fs.writeText(target, renderReportMarkdown(report), undefined, exec.signal);
463
+ }
464
+ catch (error) {
465
+ // Any write failure leaves the report in the tool result (the durable
466
+ // snapshot); the outcome reports `written: false` instead of failing.
467
+ exec.signal.throwIfAborted();
468
+ ctx.logger.debug(`dsh-doublecheck: report file write skipped: ${String(error)}`);
469
+ return { path: null, written: false };
470
+ }
471
+ return { path: target.displayPath, written: true };
472
+ }
473
+ /** Write the spec markdown through the filesystem seam, when one exists. */
474
+ async function writeSpecFile(ctx, spec, filePath, exec) {
475
+ const fs = ctx.get('fs');
476
+ if (fs === undefined)
477
+ return { path: null, written: false };
478
+ let target;
479
+ try {
480
+ target = await fs.resolve(filePath, {
481
+ ...exec.agent?.session.header.cwd !== undefined ? { cwd: exec.agent.session.header.cwd } : {},
482
+ signal: exec.signal,
483
+ });
484
+ await fs.writeText(target, renderSpecMarkdown(spec), undefined, exec.signal);
485
+ }
486
+ catch (error) {
487
+ // Any write failure leaves the spec recorded in the session (the tool
488
+ // result is the durable snapshot); the outcome reports `written: false`
489
+ // instead of failing the commit.
490
+ exec.signal.throwIfAborted();
491
+ ctx.logger.debug(`dsh-doublecheck: spec file write skipped: ${String(error)}`);
492
+ return { path: null, written: false };
493
+ }
494
+ return { path: target.displayPath, written: true };
495
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Bundled skill provider for dsh-doublecheck's own `skills/` directory.
3
+ *
4
+ * The directory keeps the generic Agent Skills layout (`<name>/SKILL.md` with
5
+ * YAML frontmatter) so the assets stay liftable into any other Agent Skills
6
+ * ecosystem; this provider is the DSH-facing adapter that publishes them on
7
+ * `ctx.skills`.
8
+ *
9
+ * @module dsh-doublecheck/grill/provider
10
+ */
11
+ import type { Context } from '@deepseek-ai/cordis';
12
+ import { type SkillCandidate, type SkillDefinition, type SkillLookupOptions, type SkillProvider } from '@deepseek-ai/dsh-skill';
13
+ /** Provider name under which the registry files these skills. */
14
+ export declare const PROVIDER_NAME = "doublecheck";
15
+ /** One parsed skill asset: metadata from the frontmatter and the instruction body. */
16
+ interface ParsedSkill {
17
+ name: string;
18
+ description: string;
19
+ whenToUse?: string;
20
+ content: string;
21
+ }
22
+ /**
23
+ * SkillProvider publishing the package's bundled `skills/` directory. List
24
+ * parses the frontmatter of every skill's SKILL.md; get re-reads the body so
25
+ * on-disk edits appear on the next load. A malformed asset is reported as an
26
+ * unreadable skill (skipped, logged), matching the registry's provider
27
+ * containment contract.
28
+ */
29
+ export declare class BundledSkillProvider implements SkillProvider {
30
+ readonly name = "doublecheck";
31
+ private readonly ctx;
32
+ constructor(ctx: Context);
33
+ /**
34
+ * Discover the bundled skill candidates.
35
+ * @param options - lookup options; `signal` cancels directory and file reads.
36
+ * @returns the complete candidate list for the bundled root.
37
+ */
38
+ list(options: SkillLookupOptions): Promise<SkillCandidate[]>;
39
+ /**
40
+ * Load a complete bundled skill body.
41
+ * @param candidate - a candidate previously returned by {@link list}.
42
+ * @param options - lookup options; `signal` cancels the read.
43
+ * @returns the full skill definition, or `undefined` when the file no longer parses.
44
+ */
45
+ get(candidate: SkillCandidate, options: SkillLookupOptions): Promise<SkillDefinition | undefined>;
46
+ private parse;
47
+ }
48
+ /**
49
+ * Parse a bundled SKILL.md asset: YAML-frontmatter scalars plus the body.
50
+ * Only the three fields this package ships are read; the frontmatter itself
51
+ * stays standard Agent Skills YAML.
52
+ * @param raw - the full file text.
53
+ * @returns the parsed skill, or `undefined` when the file has no valid frontmatter.
54
+ */
55
+ export declare function parseSkillAsset(raw: string): ParsedSkill | undefined;
56
+ export {};