@cleocode/cant 2026.5.133 → 2026.5.134
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 +18 -2
- package/dist/native-loader.d.ts +4 -2
- package/dist/native-loader.js +79 -73
- package/dist/parse.d.ts +20 -8
- package/dist/parse.js +57 -34
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -19,9 +19,15 @@ pnpm add @cleocode/cant
|
|
|
19
19
|
|
|
20
20
|
The package ships pre-built napi binaries via `optionalDependencies` for
|
|
21
21
|
`x86_64-unknown-linux-gnu`. On other platforms `@cleocode/cant` falls back to
|
|
22
|
-
|
|
22
|
+
typed errors at runtime — the TypeScript surface still loads but
|
|
23
23
|
parser/validator/executor calls require a native binding present.
|
|
24
24
|
|
|
25
|
+
**`cant-core` is the single source of truth (SSoT) for all CANT parsing.**
|
|
26
|
+
There is no JS-regex fallback parser in the routine code path. If the native
|
|
27
|
+
addon is absent, `parseCANTMessage` throws a typed error unless the
|
|
28
|
+
`CLEO_CANT_ALLOW_JS_FALLBACK=1` env-var is explicitly set (degraded mode,
|
|
29
|
+
not for production use).
|
|
30
|
+
|
|
25
31
|
## Public API
|
|
26
32
|
|
|
27
33
|
```ts
|
|
@@ -144,9 +150,19 @@ Requires a Rust toolchain. Produces `napi/cant.linux-x64-gnu.node`.
|
|
|
144
150
|
Other platform triples are added via the workspace release pipeline,
|
|
145
151
|
not locally.
|
|
146
152
|
|
|
153
|
+
## Crate track
|
|
154
|
+
|
|
155
|
+
| Crate | Status | Purpose |
|
|
156
|
+
|-------|--------|---------|
|
|
157
|
+
| `crates/cant-core` | Active — SSoT | Parser, validator, 42 static-analysis rules, pipeline executor |
|
|
158
|
+
| `crates/cant-napi` | Active | napi-rs cdylib binding wrapping cant-core + cant-runtime |
|
|
159
|
+
| `crates/cant-runtime` | Active | Deterministic pipeline execution engine (Path B) |
|
|
160
|
+
| `crates/cant-router` | Retired from napi surface (E8 T11432) | Model-tier classifier — preserved in workspace but NOT linked into the published binary |
|
|
161
|
+
| `crates/cant-lsp` | Shelved from default build (E8 T11434) | Language Server Protocol for `.cant` files — kept in workspace, build with `cargo build -p cant-lsp` |
|
|
162
|
+
|
|
147
163
|
## Related
|
|
148
164
|
|
|
149
|
-
- [`crates/cant-core`](../../crates/cant-core) — Rust
|
|
165
|
+
- [`crates/cant-core`](../../crates/cant-core) — Rust SSoT (parser, validator, pipeline executor)
|
|
150
166
|
- [`crates/cant-napi`](../../crates/cant-napi) — napi-rs bindings (cdylib)
|
|
151
167
|
- [`packages/cleo/templates/cleoos-hub/pi-extensions/cant-bridge.ts`](../cleo/templates/cleoos-hub/pi-extensions/cant-bridge.ts) — Pi-interactive runtime (Path A)
|
|
152
168
|
- [`.cleo/adrs/ADR-035-pi-v2-v3-harness.md`](../../.cleo/adrs/ADR-035-pi-v2-v3-harness.md) — architecture decisions for the CANT execution model
|
package/dist/native-loader.d.ts
CHANGED
|
@@ -270,8 +270,10 @@ export type SeedPersonaId = (typeof SEED_PERSONA_IDS)[number];
|
|
|
270
270
|
* Files that cannot be parsed (unreadable, missing `agent <id>:` block) are
|
|
271
271
|
* silently skipped.
|
|
272
272
|
*
|
|
273
|
-
*
|
|
274
|
-
*
|
|
273
|
+
* When the native addon is available, agent identity fields are extracted via
|
|
274
|
+
* `cant_extract_agent_profiles` (cant-core, E8-AC2 / T11430). When the addon
|
|
275
|
+
* is absent the loader falls back to a minimal regex extractor so basic
|
|
276
|
+
* identity resolution still works in environments without the binary.
|
|
275
277
|
*
|
|
276
278
|
* @param agentsRoot - Optional override for the `packages/agents/` root. When
|
|
277
279
|
* omitted the path is resolved automatically relative to this file. Tests
|
package/dist/native-loader.js
CHANGED
|
@@ -309,22 +309,6 @@ function resolveAgentsPackageRoot() {
|
|
|
309
309
|
];
|
|
310
310
|
return candidates.find((p) => (0, node_fs_1.existsSync)(p)) ?? null;
|
|
311
311
|
}
|
|
312
|
-
/**
|
|
313
|
-
* Extract the `role:` value from a raw `.cant` file body.
|
|
314
|
-
*
|
|
315
|
-
* Looks for a line matching ` role: <value>` within the `agent <id>:` block
|
|
316
|
-
* using a simple regex — intentionally avoids full CANT parsing to remove the
|
|
317
|
-
* native-addon dependency from this path (the addon may not be present in all
|
|
318
|
-
* environments where the loader runs).
|
|
319
|
-
*
|
|
320
|
-
* @param content - Raw `.cant` file text.
|
|
321
|
-
* @returns Extracted role string, or `null` when no `role:` line is found.
|
|
322
|
-
* @internal
|
|
323
|
-
*/
|
|
324
|
-
function extractRoleFromCant(content) {
|
|
325
|
-
const match = /^\s{2}role:\s*(\S+)/m.exec(content);
|
|
326
|
-
return match ? (match[1] ?? null) : null;
|
|
327
|
-
}
|
|
328
312
|
/**
|
|
329
313
|
* Map a raw role string from a `.cant` file to a {@link PeerKind}.
|
|
330
314
|
*
|
|
@@ -347,61 +331,22 @@ function roleToPeerKind(rawRole) {
|
|
|
347
331
|
}
|
|
348
332
|
}
|
|
349
333
|
/**
|
|
350
|
-
*
|
|
351
|
-
*
|
|
352
|
-
* Handles single-line descriptions (`description: "..."`) and multiline
|
|
353
|
-
* block-scalar descriptions (`description: |`). Returns the first line of
|
|
354
|
-
* the block-scalar body for multiline values. Returns empty string when no
|
|
355
|
-
* `description:` field is found.
|
|
356
|
-
*
|
|
357
|
-
* @param content - Raw `.cant` file text.
|
|
358
|
-
* @internal
|
|
359
|
-
*/
|
|
360
|
-
function extractDescriptionFromCant(content) {
|
|
361
|
-
// Single-line: ` description: "some text"` or ` description: some text`
|
|
362
|
-
const singleLine = /^\s{2}description:\s+"([^"]+)"/m.exec(content);
|
|
363
|
-
if (singleLine)
|
|
364
|
-
return singleLine[1] ?? '';
|
|
365
|
-
const singleLineUnquoted = /^\s{2}description:\s+(.+)/m.exec(content);
|
|
366
|
-
if (singleLineUnquoted) {
|
|
367
|
-
const raw = singleLineUnquoted[1] ?? '';
|
|
368
|
-
// Exclude multiline indicator '|'
|
|
369
|
-
if (raw.trim() !== '|')
|
|
370
|
-
return raw.trim();
|
|
371
|
-
}
|
|
372
|
-
// Multiline block scalar: grab first non-empty line after `description: |`
|
|
373
|
-
const blockScalar = /^\s{2}description:\s+\|\s*\n((?:\s+\S[^\n]*\n?)+)/m.exec(content);
|
|
374
|
-
if (blockScalar) {
|
|
375
|
-
const bodyLines = (blockScalar[1] ?? '').split('\n');
|
|
376
|
-
for (const line of bodyLines) {
|
|
377
|
-
const trimmed = line.trim();
|
|
378
|
-
if (trimmed.length > 0)
|
|
379
|
-
return trimmed;
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
return '';
|
|
383
|
-
}
|
|
384
|
-
/**
|
|
385
|
-
* Extract the agent business id from a raw `.cant` file body.
|
|
334
|
+
* Parse a single `.cant` file into a {@link PeerIdentity} via the canonical
|
|
335
|
+
* `cant_extract_agent_profiles` napi path (E8-AC2, T11430).
|
|
386
336
|
*
|
|
387
|
-
*
|
|
337
|
+
* When the native addon is available, agent id, role, and description are
|
|
338
|
+
* extracted by the Rust cant-core parser — eliminating the previous
|
|
339
|
+
* regex-based `extractRoleFromCant` / `extractAgentIdFromCant` /
|
|
340
|
+
* `extractDescriptionFromCant` helpers which are now retired.
|
|
388
341
|
*
|
|
389
|
-
*
|
|
390
|
-
*
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
const match = /^agent\s+([a-z][a-z0-9-]*):/m.exec(content);
|
|
394
|
-
return match ? (match[1] ?? null) : null;
|
|
395
|
-
}
|
|
396
|
-
/**
|
|
397
|
-
* Parse a single `.cant` file at `cantFile` into a {@link PeerIdentity}.
|
|
398
|
-
*
|
|
399
|
-
* Returns `null` when the file cannot be parsed (missing `agent <id>:` block
|
|
400
|
-
* or unreadable file). The caller is responsible for logging / skipping nulls.
|
|
342
|
+
* Falls back to a minimal regex extractor ONLY when the native addon is
|
|
343
|
+
* absent AND `fallbackId` is provided (i.e. the universal-base path where
|
|
344
|
+
* the filename is the canonical id). Returns `null` when the file is
|
|
345
|
+
* unreadable or no agent id can be determined.
|
|
401
346
|
*
|
|
402
347
|
* @param cantFile - Absolute path to the `.cant` file.
|
|
403
|
-
* @param fallbackId - Id to use when
|
|
404
|
-
* when loading the universal base by a known filename).
|
|
348
|
+
* @param fallbackId - Id to use when no `agent <id>:` block can be parsed
|
|
349
|
+
* (e.g. when loading the universal base by a known filename).
|
|
405
350
|
* @internal
|
|
406
351
|
*/
|
|
407
352
|
function parseCantFileToIdentity(cantFile, fallbackId) {
|
|
@@ -412,18 +357,77 @@ function parseCantFileToIdentity(cantFile, fallbackId) {
|
|
|
412
357
|
catch {
|
|
413
358
|
return null;
|
|
414
359
|
}
|
|
415
|
-
|
|
360
|
+
// Primary path: route through the cant-core napi bridge.
|
|
361
|
+
// The native `cantExtractAgentProfiles` returns loose objects whose shape
|
|
362
|
+
// mirrors the Rust AgentProfile extractor. Common observed fields:
|
|
363
|
+
// - `name` — agent business id (kebab-case)
|
|
364
|
+
// - `agentId` — alternative agent id field (if present)
|
|
365
|
+
// - `role` — role string (may also live inside `propertiesJson`)
|
|
366
|
+
// - `description` — agent description (may also live inside `propertiesJson`)
|
|
367
|
+
// - `propertiesJson` — JSON-serialized top-level properties blob
|
|
368
|
+
if (isNativeAvailable()) {
|
|
369
|
+
try {
|
|
370
|
+
const raw = cantExtractAgentProfilesNative(content);
|
|
371
|
+
if (Array.isArray(raw) && raw.length > 0) {
|
|
372
|
+
const entry = raw[0];
|
|
373
|
+
// Resolve agent id: prefer explicit agentId, fall back to name, then fallbackId.
|
|
374
|
+
const agentId = (typeof entry['agentId'] === 'string' ? entry['agentId'] : undefined) ??
|
|
375
|
+
(typeof entry['name'] === 'string' ? entry['name'] : undefined) ??
|
|
376
|
+
fallbackId ??
|
|
377
|
+
null;
|
|
378
|
+
if (!agentId)
|
|
379
|
+
return null;
|
|
380
|
+
// Resolve role: try direct field, then propertiesJson.
|
|
381
|
+
let rawRole = typeof entry['role'] === 'string' ? entry['role'] : null;
|
|
382
|
+
if (!rawRole && typeof entry['propertiesJson'] === 'string') {
|
|
383
|
+
try {
|
|
384
|
+
const props = JSON.parse(entry['propertiesJson']);
|
|
385
|
+
rawRole = typeof props['role'] === 'string' ? props['role'] : null;
|
|
386
|
+
}
|
|
387
|
+
catch {
|
|
388
|
+
// ignore malformed JSON
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
const peerKind = roleToPeerKind(rawRole);
|
|
392
|
+
// Resolve description: try direct field, then propertiesJson.
|
|
393
|
+
let description = typeof entry['description'] === 'string' ? entry['description'] : '';
|
|
394
|
+
if (!description && typeof entry['propertiesJson'] === 'string') {
|
|
395
|
+
try {
|
|
396
|
+
const props = JSON.parse(entry['propertiesJson']);
|
|
397
|
+
description = typeof props['description'] === 'string' ? props['description'] : '';
|
|
398
|
+
}
|
|
399
|
+
catch {
|
|
400
|
+
// ignore malformed JSON
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
return {
|
|
404
|
+
peerId: agentId,
|
|
405
|
+
peerKind,
|
|
406
|
+
cantFile,
|
|
407
|
+
displayName: agentId,
|
|
408
|
+
description,
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
catch {
|
|
413
|
+
// Fall through to regex fallback below.
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
// Degraded regex fallback — only used when the native addon is absent.
|
|
417
|
+
// Extracts the minimum fields needed to produce a usable PeerIdentity.
|
|
418
|
+
const agentIdMatch = /^agent\s+([a-z][a-z0-9-]*):/m.exec(content);
|
|
419
|
+
const agentId = (agentIdMatch !== null ? agentIdMatch[1] : undefined) ?? fallbackId ?? null;
|
|
416
420
|
if (!agentId)
|
|
417
421
|
return null;
|
|
418
|
-
const
|
|
422
|
+
const roleMatch = /^\s{2}role:\s*(\S+)/m.exec(content);
|
|
423
|
+
const rawRole = roleMatch ? (roleMatch[1] ?? null) : null;
|
|
419
424
|
const peerKind = roleToPeerKind(rawRole);
|
|
420
|
-
const description = extractDescriptionFromCant(content);
|
|
421
425
|
return {
|
|
422
426
|
peerId: agentId,
|
|
423
427
|
peerKind,
|
|
424
428
|
cantFile,
|
|
425
429
|
displayName: agentId,
|
|
426
|
-
description,
|
|
430
|
+
description: '',
|
|
427
431
|
};
|
|
428
432
|
}
|
|
429
433
|
/**
|
|
@@ -438,8 +442,10 @@ function parseCantFileToIdentity(cantFile, fallbackId) {
|
|
|
438
442
|
* Files that cannot be parsed (unreadable, missing `agent <id>:` block) are
|
|
439
443
|
* silently skipped.
|
|
440
444
|
*
|
|
441
|
-
*
|
|
442
|
-
*
|
|
445
|
+
* When the native addon is available, agent identity fields are extracted via
|
|
446
|
+
* `cant_extract_agent_profiles` (cant-core, E8-AC2 / T11430). When the addon
|
|
447
|
+
* is absent the loader falls back to a minimal regex extractor so basic
|
|
448
|
+
* identity resolution still works in environments without the binary.
|
|
443
449
|
*
|
|
444
450
|
* @param agentsRoot - Optional override for the `packages/agents/` root. When
|
|
445
451
|
* omitted the path is resolved automatically relative to this file. Tests
|
package/dist/parse.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import type { ParsedCANTMessage } from './types';
|
|
2
2
|
export type { ParsedCANTMessage };
|
|
3
3
|
/**
|
|
4
|
-
* Initialize the CANT parser
|
|
4
|
+
* Initialize the CANT parser.
|
|
5
5
|
*
|
|
6
|
-
* With napi-rs native addons
|
|
7
|
-
* Kept for backward compatibility with code that
|
|
6
|
+
* With napi-rs native addons this is a no-op — native modules load
|
|
7
|
+
* synchronously. Kept for backward compatibility with code that
|
|
8
|
+
* previously called this for WASM init.
|
|
8
9
|
*
|
|
9
10
|
* @example
|
|
10
11
|
* ```typescript
|
|
@@ -16,13 +17,24 @@ export type { ParsedCANTMessage };
|
|
|
16
17
|
*/
|
|
17
18
|
export declare function initCantParser(): Promise<void>;
|
|
18
19
|
/**
|
|
19
|
-
* Parse a CANT message
|
|
20
|
+
* Parse a CANT message via the canonical cant-core napi-rs path.
|
|
20
21
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
22
|
+
* The native addon (Rust `cant-core`) is the single source of truth for
|
|
23
|
+
* CANT message parsing (E8-AC2: one canonical path). The previous
|
|
24
|
+
* JS regex fallback is intentionally NOT a routine code path — if the
|
|
25
|
+
* native addon is unavailable the function throws a typed error so
|
|
26
|
+
* callers know they are running in a degraded environment and cannot
|
|
27
|
+
* silently produce wrong results.
|
|
23
28
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
29
|
+
* In test environments where the binary is not present, mock
|
|
30
|
+
* `isNativeAvailable` or use the `CLEO_CANT_ALLOW_JS_FALLBACK=1`
|
|
31
|
+
* environment variable to enable the clearly-marked degraded-mode
|
|
32
|
+
* branch (emits a console.warn and returns a best-effort parse).
|
|
33
|
+
*
|
|
34
|
+
* @param content - The CANT message content to parse.
|
|
35
|
+
* @returns ParsedCANTMessage with directive, addresses, task_refs, tags.
|
|
36
|
+
* @throws {Error} When the native addon is unavailable and
|
|
37
|
+
* `CLEO_CANT_ALLOW_JS_FALLBACK` is not set.
|
|
26
38
|
*
|
|
27
39
|
* @example
|
|
28
40
|
* ```typescript
|
package/dist/parse.js
CHANGED
|
@@ -4,10 +4,11 @@ exports.initCantParser = initCantParser;
|
|
|
4
4
|
exports.parseCANTMessage = parseCANTMessage;
|
|
5
5
|
const native_loader_1 = require("./native-loader");
|
|
6
6
|
/**
|
|
7
|
-
* Initialize the CANT parser
|
|
7
|
+
* Initialize the CANT parser.
|
|
8
8
|
*
|
|
9
|
-
* With napi-rs native addons
|
|
10
|
-
* Kept for backward compatibility with code that
|
|
9
|
+
* With napi-rs native addons this is a no-op — native modules load
|
|
10
|
+
* synchronously. Kept for backward compatibility with code that
|
|
11
|
+
* previously called this for WASM init.
|
|
11
12
|
*
|
|
12
13
|
* @example
|
|
13
14
|
* ```typescript
|
|
@@ -22,13 +23,24 @@ async function initCantParser() {
|
|
|
22
23
|
// Kept for backward compatibility.
|
|
23
24
|
}
|
|
24
25
|
/**
|
|
25
|
-
* Parse a CANT message
|
|
26
|
+
* Parse a CANT message via the canonical cant-core napi-rs path.
|
|
26
27
|
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
28
|
+
* The native addon (Rust `cant-core`) is the single source of truth for
|
|
29
|
+
* CANT message parsing (E8-AC2: one canonical path). The previous
|
|
30
|
+
* JS regex fallback is intentionally NOT a routine code path — if the
|
|
31
|
+
* native addon is unavailable the function throws a typed error so
|
|
32
|
+
* callers know they are running in a degraded environment and cannot
|
|
33
|
+
* silently produce wrong results.
|
|
29
34
|
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
35
|
+
* In test environments where the binary is not present, mock
|
|
36
|
+
* `isNativeAvailable` or use the `CLEO_CANT_ALLOW_JS_FALLBACK=1`
|
|
37
|
+
* environment variable to enable the clearly-marked degraded-mode
|
|
38
|
+
* branch (emits a console.warn and returns a best-effort parse).
|
|
39
|
+
*
|
|
40
|
+
* @param content - The CANT message content to parse.
|
|
41
|
+
* @returns ParsedCANTMessage with directive, addresses, task_refs, tags.
|
|
42
|
+
* @throws {Error} When the native addon is unavailable and
|
|
43
|
+
* `CLEO_CANT_ALLOW_JS_FALLBACK` is not set.
|
|
32
44
|
*
|
|
33
45
|
* @example
|
|
34
46
|
* ```typescript
|
|
@@ -40,37 +52,47 @@ async function initCantParser() {
|
|
|
40
52
|
* ```
|
|
41
53
|
*/
|
|
42
54
|
function parseCANTMessage(content) {
|
|
43
|
-
//
|
|
55
|
+
// Canonical path: use the Rust cant-core parser via napi-rs.
|
|
44
56
|
if ((0, native_loader_1.isNativeAvailable)()) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
57
|
+
const nativeResult = (0, native_loader_1.cantParseNative)(content);
|
|
58
|
+
return {
|
|
59
|
+
directive: nativeResult.directive ?? undefined,
|
|
60
|
+
directive_type: (nativeResult.directiveType?.toLowerCase() ??
|
|
61
|
+
'informational'),
|
|
62
|
+
addresses: nativeResult.addresses ?? [],
|
|
63
|
+
task_refs: nativeResult.taskRefs ?? [],
|
|
64
|
+
tags: nativeResult.tags ?? [],
|
|
65
|
+
header_raw: nativeResult.headerRaw ?? '',
|
|
66
|
+
body: nativeResult.body ?? '',
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
// ── DEGRADED MODE ────────────────────────────────────────────────────────
|
|
70
|
+
// The native addon is NOT available. This is an explicit degraded-mode
|
|
71
|
+
// branch guarded by an environment variable so it is never activated
|
|
72
|
+
// silently in production (E8-AC2). Callers that need a best-effort parse
|
|
73
|
+
// in environments without the binary (CI matrix, edge runtimes) must
|
|
74
|
+
// set CLEO_CANT_ALLOW_JS_FALLBACK=1 explicitly.
|
|
75
|
+
if (process.env.CLEO_CANT_ALLOW_JS_FALLBACK !== '1') {
|
|
76
|
+
throw new Error('cant-core native addon not available. Build it with: cargo build --release -p cant-napi\n' +
|
|
77
|
+
'Or set CLEO_CANT_ALLOW_JS_FALLBACK=1 to enable the degraded JS fallback parser.');
|
|
61
78
|
}
|
|
62
|
-
//
|
|
79
|
+
// Degraded JS fallback — header/body split with basic regex.
|
|
80
|
+
// Only reached when CLEO_CANT_ALLOW_JS_FALLBACK=1.
|
|
81
|
+
console.warn('[cant] DEGRADED MODE: cant-core native addon unavailable; using JS regex parser. ' +
|
|
82
|
+
'Results may differ from the canonical Rust parser. ' +
|
|
83
|
+
'Build cant-napi to restore full fidelity.');
|
|
63
84
|
const lines = content.split('\n');
|
|
64
|
-
const header = lines[0]
|
|
85
|
+
const header = lines[0] ?? '';
|
|
65
86
|
const body = lines.slice(1).join('\n');
|
|
66
|
-
// Basic regex extraction (not as robust as WASM parser)
|
|
67
87
|
const directiveMatch = header.match(/^\/([a-z][a-z0-9-]*)/);
|
|
68
|
-
const addresses = [...header.matchAll(/@([a-zA-Z][a-zA-Z0-9_-]*)/g)].map((m) => m[1]);
|
|
69
|
-
const taskRefs = [...content.matchAll(/T(\d+)/g)].map((m) => `T${m[1]}`);
|
|
70
|
-
const tags = [...content.matchAll(/#([a-zA-Z][a-zA-Z0-9_-]*)/g)].map((m) => m[1]);
|
|
88
|
+
const addresses = [...header.matchAll(/@([a-zA-Z][a-zA-Z0-9_-]*)/g)].map((m) => m[1] ?? '');
|
|
89
|
+
const taskRefs = [...content.matchAll(/T(\d+)/g)].map((m) => `T${m[1] ?? ''}`);
|
|
90
|
+
const tags = [...content.matchAll(/#([a-zA-Z][a-zA-Z0-9_-]*)/g)].map((m) => m[1] ?? '');
|
|
71
91
|
return {
|
|
72
92
|
directive: directiveMatch ? directiveMatch[1] : undefined,
|
|
73
|
-
directive_type: directiveMatch
|
|
93
|
+
directive_type: directiveMatch
|
|
94
|
+
? _classifyDirectiveFallback(directiveMatch[1] ?? '')
|
|
95
|
+
: 'informational',
|
|
74
96
|
addresses,
|
|
75
97
|
task_refs: taskRefs,
|
|
76
98
|
tags,
|
|
@@ -79,12 +101,13 @@ function parseCANTMessage(content) {
|
|
|
79
101
|
};
|
|
80
102
|
}
|
|
81
103
|
/**
|
|
82
|
-
* Classify a directive verb into its type
|
|
104
|
+
* Classify a directive verb into its type.
|
|
83
105
|
*
|
|
106
|
+
* @internal Used only by the degraded JS fallback parser.
|
|
84
107
|
* @param verb - The directive verb (e.g., 'done', 'action', 'info')
|
|
85
108
|
* @returns 'actionable', 'routing', or 'informational'
|
|
86
109
|
*/
|
|
87
|
-
function
|
|
110
|
+
function _classifyDirectiveFallback(verb) {
|
|
88
111
|
const actionable = ['claim', 'done', 'blocked', 'approve', 'decision', 'checkin'];
|
|
89
112
|
const routing = ['action', 'review', 'proposal'];
|
|
90
113
|
if (actionable.includes(verb))
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cleocode/cant",
|
|
3
|
-
"version": "2026.5.
|
|
4
|
-
"description": "CANT
|
|
3
|
+
"version": "2026.5.134",
|
|
4
|
+
"description": "CANT DSL — cant-core (SSoT) parser, validator, and pipeline executor via napi-rs; agent identity, message parsing, and migration utilities",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"files": [
|
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
"napi/"
|
|
10
10
|
],
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@cleocode/contracts": "2026.5.
|
|
13
|
-
"@cleocode/core": "2026.5.
|
|
14
|
-
"@cleocode/lafs": "2026.5.
|
|
12
|
+
"@cleocode/contracts": "2026.5.134",
|
|
13
|
+
"@cleocode/core": "2026.5.134",
|
|
14
|
+
"@cleocode/lafs": "2026.5.134"
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {
|
|
17
17
|
"typescript": "^6.0.2",
|