@cleocode/contracts 2026.7.2 → 2026.8.2
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/dist/__tests__/evidence-atom-schema.test.js +139 -1
- package/dist/__tests__/evidence-atom-schema.test.js.map +1 -1
- package/dist/caamp-markers.d.ts +99 -0
- package/dist/caamp-markers.d.ts.map +1 -0
- package/dist/caamp-markers.js +121 -0
- package/dist/caamp-markers.js.map +1 -0
- package/dist/changesets.d.ts +2 -2
- package/dist/evidence-atom-schema.d.ts +16 -0
- package/dist/evidence-atom-schema.d.ts.map +1 -1
- package/dist/evidence-atom-schema.js +50 -3
- package/dist/evidence-atom-schema.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/project-context.d.ts +29 -0
- package/dist/project-context.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/evidence-atom-schema.test.ts +167 -0
- package/src/caamp-markers.ts +149 -0
- package/src/evidence-atom-schema.ts +54 -3
- package/src/index.ts +13 -0
- package/src/project-context.ts +30 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CAAMP instruction-block marker grammar — the single source of truth.
|
|
3
|
+
*
|
|
4
|
+
* CAAMP delimits the region it owns inside an agent instruction file
|
|
5
|
+
* (`AGENTS.md`, `CLAUDE.md`, `GEMINI.md`, …) with a pair of HTML comments:
|
|
6
|
+
*
|
|
7
|
+
* ```markdown
|
|
8
|
+
* <!-- CAAMP:START -->
|
|
9
|
+
* @~/.cleo/templates/CLEO-INJECTION.md
|
|
10
|
+
* <!-- CAAMP:END -->
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* Before T12051 this grammar was re-declared as inline string and regex
|
|
14
|
+
* literals in at least five places — `caamp/core/instructions/injector.ts`,
|
|
15
|
+
* `caamp/core/harness/pi.ts`, `core/injection.ts`, `core/bootstrap.ts` and
|
|
16
|
+
* `core/validation/doctor/checks.ts`. Each copy recognised a slightly
|
|
17
|
+
* different dialect, so a file that one module considered healthy another
|
|
18
|
+
* considered corrupt.
|
|
19
|
+
*
|
|
20
|
+
* The grammar lives here, in the leaf package, because `@cleocode/core` and
|
|
21
|
+
* `@cleocode/caamp` depend on each other and therefore cannot import the
|
|
22
|
+
* grammar from one another without a cycle.
|
|
23
|
+
*
|
|
24
|
+
* This module is **const data only** — no runtime helpers — so it satisfies
|
|
25
|
+
* the contracts-purity gate (`scripts/lint-no-runtime-in-contracts.mjs`).
|
|
26
|
+
* The engine that consumes these patterns lives in
|
|
27
|
+
* `packages/caamp/src/core/instructions/markers.ts`.
|
|
28
|
+
*
|
|
29
|
+
* @task T12051
|
|
30
|
+
* @see {@link https://github.com/kryptobaseddev/cleo} ADR-064 (CAAMP↔Adapters boundary)
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
/** Canonical opening marker of a CAAMP-managed block. */
|
|
34
|
+
export const CAAMP_MARKER_START = '<!-- CAAMP:START -->';
|
|
35
|
+
|
|
36
|
+
/** Canonical closing marker of a CAAMP-managed block. */
|
|
37
|
+
export const CAAMP_MARKER_END = '<!-- CAAMP:END -->';
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Source of the strict, canonical block pattern.
|
|
41
|
+
*
|
|
42
|
+
* Capture group 1 is the block's inner content. Build a fresh `RegExp` from
|
|
43
|
+
* this string at every use site — a module-level `RegExp` carrying the `g`
|
|
44
|
+
* flag keeps a mutable `lastIndex`, which silently skips matches when the
|
|
45
|
+
* same object is reused across `.test()` / `.exec()` calls.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```typescript
|
|
49
|
+
* const pattern = new RegExp(CAAMP_BLOCK_PATTERN_SOURCE, 'g');
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
export const CAAMP_BLOCK_PATTERN_SOURCE = '<!-- CAAMP:START -->([\\s\\S]*?)<!-- CAAMP:END -->';
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Horizontal-whitespace class used by the damage-tolerant marker patterns.
|
|
56
|
+
*
|
|
57
|
+
* Deliberately excludes `\n` so the patterns stay anchored to a single line
|
|
58
|
+
* and cannot swallow surrounding content.
|
|
59
|
+
*/
|
|
60
|
+
const H = '[ \\t\\r]';
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Build the source of a damage-tolerant, whole-line marker pattern.
|
|
64
|
+
*
|
|
65
|
+
* Recognises the canonical marker plus the near-miss forms produced when a
|
|
66
|
+
* delimiter character is lost or mangled — a truncated write, an editor that
|
|
67
|
+
* reflows HTML comments, a shell heredoc, a careless hand edit.
|
|
68
|
+
*
|
|
69
|
+
* Two constraints keep this from over-matching, both learned from an
|
|
70
|
+
* adversarial review that demonstrated content loss with a looser pattern:
|
|
71
|
+
*
|
|
72
|
+
* 1. The `CAAMP:<KEYWORD>` token must be the *entire* line, modulo comment
|
|
73
|
+
* punctuation and horizontal whitespace. Prose such as "the CAAMP:START
|
|
74
|
+
* marker is written by …" is therefore never matched.
|
|
75
|
+
* 2. Comment punctuation must be present on **both** sides — at least one of
|
|
76
|
+
* `< ! -` before the token and at least one of `- >` after it. A bare
|
|
77
|
+
* `CAAMP:START` line is deliberately NOT treated as a damaged marker: it is
|
|
78
|
+
* not a plausible outcome of losing one delimiter character, and accepting
|
|
79
|
+
* it caused a documentation fence that merely *mentioned* CAAMP to be
|
|
80
|
+
* rewritten into a real marker, silently swallowing the fenced body.
|
|
81
|
+
*/
|
|
82
|
+
const damagedMarkerSource = (keyword: 'START' | 'END'): string =>
|
|
83
|
+
`^${H}*[<!-]{1,4}${H}*CAAMP${H}*:${H}*${keyword}${H}*[->]{1,3}${H}*$`;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Source of the damage-tolerant pattern matching an opening marker line.
|
|
87
|
+
*
|
|
88
|
+
* Use with the `gmi` flags. Matches all of:
|
|
89
|
+
*
|
|
90
|
+
* | Form | Damage |
|
|
91
|
+
* | ------------------------ | -------------------------- |
|
|
92
|
+
* | `<!-- CAAMP:START -->` | none (canonical) |
|
|
93
|
+
* | `!-- CAAMP:START -->` | leading `<` lost |
|
|
94
|
+
* | `<!-- CAAMP:START --` | trailing `>` lost |
|
|
95
|
+
* | `<!--CAAMP:START-->` | spaces collapsed |
|
|
96
|
+
* | `<!-- caamp:start -->` | case folded |
|
|
97
|
+
*
|
|
98
|
+
* A bare `CAAMP:START` with no comment punctuation at all is deliberately NOT
|
|
99
|
+
* matched — see {@link damagedMarkerSource}.
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* ```typescript
|
|
103
|
+
* const damaged = new RegExp(CAAMP_DAMAGED_START_PATTERN_SOURCE, 'gmi');
|
|
104
|
+
* const healed = content.replace(damaged, CAAMP_MARKER_START);
|
|
105
|
+
* ```
|
|
106
|
+
*/
|
|
107
|
+
export const CAAMP_DAMAGED_START_PATTERN_SOURCE = damagedMarkerSource('START');
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Source of the damage-tolerant pattern matching a closing marker line.
|
|
111
|
+
*
|
|
112
|
+
* Mirrors {@link CAAMP_DAMAGED_START_PATTERN_SOURCE} for `CAAMP:END`. Use
|
|
113
|
+
* with the `gmi` flags.
|
|
114
|
+
*/
|
|
115
|
+
export const CAAMP_DAMAGED_END_PATTERN_SOURCE = damagedMarkerSource('END');
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* The outcome of writing a CAAMP block into an instruction file.
|
|
119
|
+
*
|
|
120
|
+
* Previously re-declared as an inline union in eight separate locations.
|
|
121
|
+
*
|
|
122
|
+
* - `created` — the file did not exist and was written from scratch.
|
|
123
|
+
* - `added` — the file existed with no CAAMP block; one was prepended.
|
|
124
|
+
* - `repaired` — at least one damaged marker was healed back to canonical form.
|
|
125
|
+
* - `consolidated` — several blocks were merged down to exactly one.
|
|
126
|
+
* - `updated` — a single block existed and its content changed.
|
|
127
|
+
* - `intact` — the file already had exactly the desired content; no write.
|
|
128
|
+
*/
|
|
129
|
+
export type CaampInjectionAction =
|
|
130
|
+
| 'created'
|
|
131
|
+
| 'added'
|
|
132
|
+
| 'repaired'
|
|
133
|
+
| 'consolidated'
|
|
134
|
+
| 'updated'
|
|
135
|
+
| 'intact';
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Every {@link CaampInjectionAction} value, ordered from most to least
|
|
139
|
+
* invasive. Useful for reporting the single most significant action taken
|
|
140
|
+
* across a batch of files.
|
|
141
|
+
*/
|
|
142
|
+
export const CAAMP_INJECTION_ACTIONS = [
|
|
143
|
+
'created',
|
|
144
|
+
'added',
|
|
145
|
+
'repaired',
|
|
146
|
+
'consolidated',
|
|
147
|
+
'updated',
|
|
148
|
+
'intact',
|
|
149
|
+
] as const;
|
|
@@ -33,6 +33,52 @@ import { z } from 'zod';
|
|
|
33
33
|
|
|
34
34
|
import type { VerificationGate } from './task.js';
|
|
35
35
|
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
// Known atom kinds — shared typed constant for boundary detection + error
|
|
38
|
+
// messages. Prefer this list over duplicated regex strings.
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Every evidence atom kind recognized by the parser.
|
|
43
|
+
*
|
|
44
|
+
* Defines the exhaustive set of `kind:` prefixes accepted by
|
|
45
|
+
* {@link parseEvidenceString}. Derived from {@link EvidenceAtomSchema}'s
|
|
46
|
+
* discriminated union. Shared so boundary-splitting regexes, documentation,
|
|
47
|
+
* and error messages always agree without duplicating the literal list.
|
|
48
|
+
*
|
|
49
|
+
* Note: `state` is a recognized parser prefix (modifier for `pr:`) but is
|
|
50
|
+
* NOT a standalone {@link EvidenceAtom} kind — it is consumed in-place by the
|
|
51
|
+
* parser and never emitted as an atom. The boundary regex MUST include it so
|
|
52
|
+
* `pr:357;state:MERGED` splits correctly.
|
|
53
|
+
*
|
|
54
|
+
* @task T12030
|
|
55
|
+
*/
|
|
56
|
+
export const EVIDENCE_ATOM_KINDS = [
|
|
57
|
+
'commit',
|
|
58
|
+
'files',
|
|
59
|
+
'test-run',
|
|
60
|
+
'tool',
|
|
61
|
+
'url',
|
|
62
|
+
'note',
|
|
63
|
+
'decision',
|
|
64
|
+
'pr',
|
|
65
|
+
'loc-drop',
|
|
66
|
+
'callsite-coverage',
|
|
67
|
+
'satisfies',
|
|
68
|
+
] as const satisfies ReadonlyArray<EvidenceAtom['kind']>;
|
|
69
|
+
|
|
70
|
+
/** Parser-level prefixes — {@link EVIDENCE_ATOM_KINDS} + `state` modifier. */
|
|
71
|
+
const PARSER_PREFIXES = [...EVIDENCE_ATOM_KINDS, 'state'] as const;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Regex matching a `;` ONLY when the next non-whitespace token is a known
|
|
75
|
+
* atom kind prefix followed by `:`. Used instead of a naive `split(';')` so
|
|
76
|
+
* semicolons inside `note:` payloads are preserved.
|
|
77
|
+
*
|
|
78
|
+
* @task T12030
|
|
79
|
+
*/
|
|
80
|
+
const KIND_BOUNDARY_REGEX = new RegExp(`;(?=\\s*(?:${PARSER_PREFIXES.join('|')})\\s*:)`, 'g');
|
|
81
|
+
|
|
36
82
|
// ---------------------------------------------------------------------------
|
|
37
83
|
// Per-atom Zod schemas
|
|
38
84
|
// ---------------------------------------------------------------------------
|
|
@@ -714,8 +760,9 @@ export function parseEvidenceString(raw: string): EvidenceAtom[] {
|
|
|
714
760
|
);
|
|
715
761
|
}
|
|
716
762
|
const chunks = raw
|
|
717
|
-
.split(
|
|
763
|
+
.split(KIND_BOUNDARY_REGEX)
|
|
718
764
|
.map((s) => s.trim())
|
|
765
|
+
.map((s) => s.replace(/^;+/, '')) // strip leading separator artifacts (;;commit: → commit:)
|
|
719
766
|
.filter(Boolean);
|
|
720
767
|
if (chunks.length === 0) {
|
|
721
768
|
throw new EvidenceParseError(
|
|
@@ -734,7 +781,11 @@ export function parseEvidenceString(raw: string): EvidenceAtom[] {
|
|
|
734
781
|
);
|
|
735
782
|
}
|
|
736
783
|
const kind = chunk.slice(0, colon).trim();
|
|
737
|
-
const
|
|
784
|
+
const rawPayload = chunk.slice(colon + 1).trim();
|
|
785
|
+
// Strip trailing separator semicolons from non-note atoms (the old naive
|
|
786
|
+
// split consumed them as empty chunks). Note payloads preserve every
|
|
787
|
+
// character, including trailing ';', so they act as real punctuation.
|
|
788
|
+
const payload = kind === 'note' ? rawPayload : rawPayload.replace(/;+$/, '');
|
|
738
789
|
switch (kind) {
|
|
739
790
|
case 'commit':
|
|
740
791
|
atoms.push({ kind: 'commit', sha: payload });
|
|
@@ -930,7 +981,7 @@ export function parseEvidenceString(raw: string): EvidenceAtom[] {
|
|
|
930
981
|
default:
|
|
931
982
|
throw new EvidenceParseError(
|
|
932
983
|
`Unknown evidence kind: "${kind}" in atom "${chunk}"`,
|
|
933
|
-
|
|
984
|
+
`Valid kinds: ${PARSER_PREFIXES.join(', ')}`,
|
|
934
985
|
);
|
|
935
986
|
}
|
|
936
987
|
}
|
package/src/index.ts
CHANGED
|
@@ -544,6 +544,7 @@ export {
|
|
|
544
544
|
callsiteCoverageAtomSchema,
|
|
545
545
|
commitAtomSchema,
|
|
546
546
|
decisionAtomSchema,
|
|
547
|
+
EVIDENCE_ATOM_KINDS,
|
|
547
548
|
EvidenceAtomSchema,
|
|
548
549
|
EvidenceParseError,
|
|
549
550
|
filesAtomSchema,
|
|
@@ -2652,4 +2653,16 @@ export {
|
|
|
2652
2653
|
workGraphSliceParamsSchema,
|
|
2653
2654
|
workGraphSliceSchema,
|
|
2654
2655
|
} from './workgraph.js';
|
|
2656
|
+
|
|
2655
2657
|
// === WASM SDK (Rust crate bindings) ===
|
|
2658
|
+
|
|
2659
|
+
export type { CaampInjectionAction } from './caamp-markers.js';
|
|
2660
|
+
// === CAAMP instruction-block marker grammar (T12051) ===
|
|
2661
|
+
export {
|
|
2662
|
+
CAAMP_BLOCK_PATTERN_SOURCE,
|
|
2663
|
+
CAAMP_DAMAGED_END_PATTERN_SOURCE,
|
|
2664
|
+
CAAMP_DAMAGED_START_PATTERN_SOURCE,
|
|
2665
|
+
CAAMP_INJECTION_ACTIONS,
|
|
2666
|
+
CAAMP_MARKER_END,
|
|
2667
|
+
CAAMP_MARKER_START,
|
|
2668
|
+
} from './caamp-markers.js';
|
package/src/project-context.ts
CHANGED
|
@@ -55,6 +55,14 @@ export type FileNamingConvention = 'kebab-case' | 'snake_case' | 'camelCase' | '
|
|
|
55
55
|
/** Module import style. */
|
|
56
56
|
export type ImportStyle = 'esm' | 'commonjs' | 'mixed';
|
|
57
57
|
|
|
58
|
+
/** Release and evidence behavior configured per project. */
|
|
59
|
+
export interface ProjectReleaseContext {
|
|
60
|
+
/** Branches whose reachable commits may satisfy `commit:` evidence atoms. */
|
|
61
|
+
integrationBranches?: string[];
|
|
62
|
+
/** Required successful checks for `pr:` evidence atoms. */
|
|
63
|
+
prRequiredWorkflows?: string[];
|
|
64
|
+
}
|
|
65
|
+
|
|
58
66
|
/** Schema-compliant project context for LLM agent consumption. */
|
|
59
67
|
export interface ProjectContext {
|
|
60
68
|
schemaVersion: string;
|
|
@@ -91,6 +99,28 @@ export interface ProjectContext {
|
|
|
91
99
|
commonPatterns?: string[];
|
|
92
100
|
avoidPatterns?: string[];
|
|
93
101
|
};
|
|
102
|
+
/** Lint command override (T12026). */
|
|
103
|
+
lint?: {
|
|
104
|
+
/** Command to run static analysis / lint checks. */
|
|
105
|
+
command?: string;
|
|
106
|
+
};
|
|
107
|
+
/** Type-check command override (T12026). */
|
|
108
|
+
typecheck?: {
|
|
109
|
+
/** Command to run type checking. */
|
|
110
|
+
command?: string;
|
|
111
|
+
};
|
|
112
|
+
/** Audit command override (T12026). */
|
|
113
|
+
audit?: {
|
|
114
|
+
/** Command to run dependency/module audit checks. */
|
|
115
|
+
command?: string;
|
|
116
|
+
};
|
|
117
|
+
/** Security-scan command override (T12026). JSON key is `security-scan`. */
|
|
118
|
+
'security-scan'?: {
|
|
119
|
+
/** Command to run security vulnerability scanning. */
|
|
120
|
+
command?: string;
|
|
121
|
+
};
|
|
122
|
+
/** Release and evidence behavior for this project. */
|
|
123
|
+
release?: ProjectReleaseContext;
|
|
94
124
|
}
|
|
95
125
|
|
|
96
126
|
/**
|