@cleocode/cant 2026.3.76

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.
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Migration types for markdown-to-CANT conversion.
3
+ *
4
+ * Defines the input/output contracts for the `cant migrate` command
5
+ * and the underlying conversion engine.
6
+ */
7
+ /** Options controlling migration behavior. */
8
+ export interface MigrationOptions {
9
+ /** Write .cant files to disk (false = dry-run preview). */
10
+ write: boolean;
11
+ /** Show detailed conversion log during migration. */
12
+ verbose: boolean;
13
+ /** Where to write .cant files (default: .cleo/agents/). */
14
+ outputDir?: string;
15
+ }
16
+ /** Result of migrating a single markdown file. */
17
+ export interface MigrationResult {
18
+ /** Absolute path to the input markdown file. */
19
+ inputFile: string;
20
+ /** Array of .cant files produced (or that would be produced in dry-run). */
21
+ outputFiles: ConvertedFile[];
22
+ /** Sections that could not be automatically converted. */
23
+ unconverted: UnconvertedSection[];
24
+ /** Human-readable summary string. */
25
+ summary: string;
26
+ }
27
+ /** A single converted .cant output file. */
28
+ export interface ConvertedFile {
29
+ /** Relative path for the output file (e.g. ".cleo/agents/code-reviewer.cant"). */
30
+ path: string;
31
+ /** Document kind: agent, skill, hook, or workflow. */
32
+ kind: string;
33
+ /** The full .cant file content including frontmatter. */
34
+ content: string;
35
+ }
36
+ /** A section of markdown that was not converted. */
37
+ export interface UnconvertedSection {
38
+ /** 1-based line number where the section starts. */
39
+ lineStart: number;
40
+ /** 1-based line number where the section ends. */
41
+ lineEnd: number;
42
+ /** Human-readable reason why conversion was skipped. */
43
+ reason: string;
44
+ /** The raw markdown content of the section. */
45
+ content: string;
46
+ }
47
+ /**
48
+ * A parsed markdown section identified by heading.
49
+ *
50
+ * Used internally by the markdown parser to structure
51
+ * the input before classification and conversion.
52
+ */
53
+ export interface MarkdownSection {
54
+ /** The heading text (without the `#` prefix). */
55
+ heading: string;
56
+ /** The heading level (2 for ##, 3 for ###, etc.). */
57
+ level: number;
58
+ /** 1-based line number where the section starts. */
59
+ lineStart: number;
60
+ /** 1-based line number where the section ends (inclusive). */
61
+ lineEnd: number;
62
+ /** Lines of content below the heading (excluding the heading line). */
63
+ bodyLines: string[];
64
+ /** Classified type of this section, determined by heuristic matching. */
65
+ classification: SectionClassification;
66
+ }
67
+ /** Classification of a markdown section for conversion purposes. */
68
+ export type SectionClassification = 'agent' | 'permissions' | 'hook' | 'skill' | 'workflow' | 'unknown';
69
+ /**
70
+ * A key-value property extracted from a markdown bullet list.
71
+ *
72
+ * Matches patterns like `- **Key**: value` or `- Key: value`.
73
+ */
74
+ export interface ExtractedProperty {
75
+ /** Property key (lowercased, normalized). */
76
+ key: string;
77
+ /** Property value (trimmed). */
78
+ value: string;
79
+ }
80
+ /**
81
+ * A permission entry extracted from markdown.
82
+ *
83
+ * Matches patterns like `- Tasks: read, write` or `- Read and write tasks`.
84
+ */
85
+ export interface ExtractedPermission {
86
+ /** The domain (e.g. "tasks", "session", "memory"). */
87
+ domain: string;
88
+ /** Permission values (e.g. ["read", "write"]). */
89
+ values: string[];
90
+ }
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ /**
3
+ * Migration types for markdown-to-CANT conversion.
4
+ *
5
+ * Defines the input/output contracts for the `cant migrate` command
6
+ * and the underlying conversion engine.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Native addon loader for cant-core via napi-rs
3
+ *
4
+ * Loads the napi-rs native addon synchronously. Falls back gracefully
5
+ * if the native addon is not available (e.g., unsupported platform).
6
+ *
7
+ * Replaces the previous wasm-loader.ts which used async WASM initialization.
8
+ */
9
+ /**
10
+ * Check if the native addon is available
11
+ */
12
+ export declare function isNativeAvailable(): boolean;
13
+ /**
14
+ * Parse a CANT message using the native addon
15
+ */
16
+ export declare function cantParseNative(content: string): unknown;
17
+ /**
18
+ * Classify a directive using the native addon
19
+ */
20
+ export declare function cantClassifyDirectiveNative(verb: string): string;
21
+ export declare const isWasmAvailable: typeof isNativeAvailable;
22
+ export declare const initWasm: () => Promise<void>;
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ /**
3
+ * Native addon loader for cant-core via napi-rs
4
+ *
5
+ * Loads the napi-rs native addon synchronously. Falls back gracefully
6
+ * if the native addon is not available (e.g., unsupported platform).
7
+ *
8
+ * Replaces the previous wasm-loader.ts which used async WASM initialization.
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.initWasm = exports.isWasmAvailable = void 0;
12
+ exports.isNativeAvailable = isNativeAvailable;
13
+ exports.cantParseNative = cantParseNative;
14
+ exports.cantClassifyDirectiveNative = cantClassifyDirectiveNative;
15
+ let nativeModule = null;
16
+ let loadAttempted = false;
17
+ /**
18
+ * Attempt to load the native addon. Called lazily on first use.
19
+ * Native addons load synchronously via require() — no async init needed.
20
+ */
21
+ function ensureLoaded() {
22
+ if (loadAttempted)
23
+ return;
24
+ loadAttempted = true;
25
+ try {
26
+ // Try loading the napi-rs native addon
27
+ // The package name will be @cleocode/cant-native once published
28
+ nativeModule = require('@cleocode/cant-native');
29
+ }
30
+ catch {
31
+ try {
32
+ // Development fallback: try loading from the crate build output
33
+ nativeModule = require('../../crates/cant-napi');
34
+ }
35
+ catch {
36
+ // Native addon not available — JS fallback will be used
37
+ nativeModule = null;
38
+ }
39
+ }
40
+ }
41
+ /**
42
+ * Check if the native addon is available
43
+ */
44
+ function isNativeAvailable() {
45
+ ensureLoaded();
46
+ return nativeModule !== null;
47
+ }
48
+ /**
49
+ * Parse a CANT message using the native addon
50
+ */
51
+ function cantParseNative(content) {
52
+ ensureLoaded();
53
+ if (!nativeModule) {
54
+ throw new Error('Native addon not available.');
55
+ }
56
+ return nativeModule.cantParse(content);
57
+ }
58
+ /**
59
+ * Classify a directive using the native addon
60
+ */
61
+ function cantClassifyDirectiveNative(verb) {
62
+ ensureLoaded();
63
+ if (!nativeModule) {
64
+ throw new Error('Native addon not available.');
65
+ }
66
+ return nativeModule.cantClassifyDirective(verb);
67
+ }
68
+ // Backward compatibility aliases
69
+ exports.isWasmAvailable = isNativeAvailable;
70
+ const initWasm = async () => {
71
+ ensureLoaded();
72
+ };
73
+ exports.initWasm = initWasm;
@@ -0,0 +1,36 @@
1
+ import type { ParsedCANTMessage } from './types';
2
+ export type { ParsedCANTMessage };
3
+ /**
4
+ * Initialize the CANT parser
5
+ *
6
+ * With napi-rs native addons, this is a no-op (native modules load synchronously).
7
+ * Kept for backward compatibility with code that previously called this for WASM init.
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * import { initCantParser, parseCANTMessage } from '@cleocode/cant';
12
+ *
13
+ * await initCantParser(); // no-op, kept for compat
14
+ * const result = parseCANTMessage('/done @all T1234');
15
+ * ```
16
+ */
17
+ export declare function initCantParser(): Promise<void>;
18
+ /**
19
+ * Parse a CANT message
20
+ *
21
+ * If the native addon is available, uses the Rust cant-core parser via napi-rs.
22
+ * Falls back to a basic JavaScript implementation if the native addon is not loaded.
23
+ *
24
+ * @param content - The CANT message content to parse
25
+ * @returns ParsedCANTMessage with directive, addresses, task_refs, tags
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * const result = parseCANTMessage('/done @all T1234 #shipped');
30
+ * console.log(result.directive); // 'done'
31
+ * console.log(result.addresses); // ['all']
32
+ * console.log(result.task_refs); // ['T1234']
33
+ * console.log(result.tags); // ['shipped']
34
+ * ```
35
+ */
36
+ export declare function parseCANTMessage(content: string): ParsedCANTMessage;
package/dist/parse.js ADDED
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.initCantParser = initCantParser;
4
+ exports.parseCANTMessage = parseCANTMessage;
5
+ const native_loader_1 = require("./native-loader");
6
+ /**
7
+ * Initialize the CANT parser
8
+ *
9
+ * With napi-rs native addons, this is a no-op (native modules load synchronously).
10
+ * Kept for backward compatibility with code that previously called this for WASM init.
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * import { initCantParser, parseCANTMessage } from '@cleocode/cant';
15
+ *
16
+ * await initCantParser(); // no-op, kept for compat
17
+ * const result = parseCANTMessage('/done @all T1234');
18
+ * ```
19
+ */
20
+ async function initCantParser() {
21
+ // No-op: napi-rs native addons load synchronously via require().
22
+ // Kept for backward compatibility.
23
+ }
24
+ /**
25
+ * Parse a CANT message
26
+ *
27
+ * If the native addon is available, uses the Rust cant-core parser via napi-rs.
28
+ * Falls back to a basic JavaScript implementation if the native addon is not loaded.
29
+ *
30
+ * @param content - The CANT message content to parse
31
+ * @returns ParsedCANTMessage with directive, addresses, task_refs, tags
32
+ *
33
+ * @example
34
+ * ```typescript
35
+ * const result = parseCANTMessage('/done @all T1234 #shipped');
36
+ * console.log(result.directive); // 'done'
37
+ * console.log(result.addresses); // ['all']
38
+ * console.log(result.task_refs); // ['T1234']
39
+ * console.log(result.tags); // ['shipped']
40
+ * ```
41
+ */
42
+ function parseCANTMessage(content) {
43
+ // If native addon is available, use it
44
+ if ((0, native_loader_1.isNativeAvailable)()) {
45
+ try {
46
+ const nativeResult = (0, native_loader_1.cantParseNative)(content);
47
+ return {
48
+ directive: nativeResult.directive ?? undefined,
49
+ directive_type: (nativeResult.directiveType?.toLowerCase() ??
50
+ 'informational'),
51
+ addresses: nativeResult.addresses ?? [],
52
+ task_refs: nativeResult.taskRefs ?? [],
53
+ tags: nativeResult.tags ?? [],
54
+ header_raw: nativeResult.headerRaw ?? '',
55
+ body: nativeResult.body ?? '',
56
+ };
57
+ }
58
+ catch (error) {
59
+ console.warn('Native parsing failed, falling back to JS:', error);
60
+ }
61
+ }
62
+ // Fallback: basic JS implementation (header/body split)
63
+ const lines = content.split('\n');
64
+ const header = lines[0] || '';
65
+ const body = lines.slice(1).join('\n');
66
+ // Basic regex extraction (not as robust as WASM parser)
67
+ 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]);
71
+ return {
72
+ directive: directiveMatch ? directiveMatch[1] : undefined,
73
+ directive_type: directiveMatch ? classifyDirective(directiveMatch[1]) : 'informational',
74
+ addresses,
75
+ task_refs: taskRefs,
76
+ tags,
77
+ header_raw: header,
78
+ body,
79
+ };
80
+ }
81
+ /**
82
+ * Classify a directive verb into its type
83
+ *
84
+ * @param verb - The directive verb (e.g., 'done', 'action', 'info')
85
+ * @returns 'actionable', 'routing', or 'informational'
86
+ */
87
+ function classifyDirective(verb) {
88
+ const actionable = ['claim', 'done', 'blocked', 'approve', 'decision', 'checkin'];
89
+ const routing = ['action', 'review', 'proposal'];
90
+ if (actionable.includes(verb))
91
+ return 'actionable';
92
+ if (routing.includes(verb))
93
+ return 'routing';
94
+ return 'informational';
95
+ }
@@ -0,0 +1,10 @@
1
+ export type DirectiveType = 'actionable' | 'routing' | 'informational';
2
+ export interface ParsedCANTMessage {
3
+ directive?: string;
4
+ directive_type: DirectiveType;
5
+ addresses: string[];
6
+ task_refs: string[];
7
+ tags: string[];
8
+ header_raw: string;
9
+ body: string;
10
+ }
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,31 @@
1
+ /**
2
+ * WASM loader for cant-core
3
+ *
4
+ * Loads the WASM module and provides access to CANT parsing functions
5
+ */
6
+ /** Shape of the CANT WASM module exports. */
7
+ interface CantWasmModule {
8
+ default(): Promise<void>;
9
+ cant_parse(content: string): unknown;
10
+ cant_classify_directive(verb: string): string;
11
+ }
12
+ declare let wasmModule: CantWasmModule | null;
13
+ /**
14
+ * Initialize the WASM module
15
+ * Must be called before using any WASM functions
16
+ */
17
+ export declare function initWasm(): Promise<void>;
18
+ /**
19
+ * Check if WASM is available
20
+ */
21
+ export declare function isWasmAvailable(): boolean;
22
+ /**
23
+ * Parse a CANT message using WASM
24
+ */
25
+ export declare function cantParseWASM(content: string): unknown;
26
+ /**
27
+ * Classify a directive using WASM
28
+ */
29
+ export declare function cantClassifyDirectiveWASM(verb: string): string;
30
+ export type { CantWasmModule };
31
+ export { wasmModule };
@@ -0,0 +1,100 @@
1
+ "use strict";
2
+ /**
3
+ * WASM loader for cant-core
4
+ *
5
+ * Loads the WASM module and provides access to CANT parsing functions
6
+ */
7
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
+ if (k2 === undefined) k2 = k;
9
+ var desc = Object.getOwnPropertyDescriptor(m, k);
10
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
+ desc = { enumerable: true, get: function() { return m[k]; } };
12
+ }
13
+ Object.defineProperty(o, k2, desc);
14
+ }) : (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ o[k2] = m[k];
17
+ }));
18
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
19
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
20
+ }) : function(o, v) {
21
+ o["default"] = v;
22
+ });
23
+ var __importStar = (this && this.__importStar) || (function () {
24
+ var ownKeys = function(o) {
25
+ ownKeys = Object.getOwnPropertyNames || function (o) {
26
+ var ar = [];
27
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
28
+ return ar;
29
+ };
30
+ return ownKeys(o);
31
+ };
32
+ return function (mod) {
33
+ if (mod && mod.__esModule) return mod;
34
+ var result = {};
35
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
36
+ __setModuleDefault(result, mod);
37
+ return result;
38
+ };
39
+ })();
40
+ Object.defineProperty(exports, "__esModule", { value: true });
41
+ exports.wasmModule = void 0;
42
+ exports.initWasm = initWasm;
43
+ exports.isWasmAvailable = isWasmAvailable;
44
+ exports.cantParseWASM = cantParseWASM;
45
+ exports.cantClassifyDirectiveWASM = cantClassifyDirectiveWASM;
46
+ // Dynamic import of the WASM module
47
+ let wasmModule = null;
48
+ exports.wasmModule = wasmModule;
49
+ let isInitializing = false;
50
+ let initPromise = null;
51
+ /**
52
+ * Initialize the WASM module
53
+ * Must be called before using any WASM functions
54
+ */
55
+ async function initWasm() {
56
+ if (wasmModule)
57
+ return;
58
+ if (isInitializing) {
59
+ return initPromise;
60
+ }
61
+ isInitializing = true;
62
+ initPromise = (async () => {
63
+ try {
64
+ // Try to load the WASM module
65
+ const wasm = (await Promise.resolve().then(() => __importStar(require('../wasm/cant_core'))));
66
+ await wasm.default();
67
+ exports.wasmModule = wasmModule = wasm;
68
+ }
69
+ catch (_error) {
70
+ console.warn('WASM module not available, falling back to stub implementation');
71
+ exports.wasmModule = wasmModule = null;
72
+ }
73
+ })();
74
+ await initPromise;
75
+ isInitializing = false;
76
+ }
77
+ /**
78
+ * Check if WASM is available
79
+ */
80
+ function isWasmAvailable() {
81
+ return wasmModule !== null;
82
+ }
83
+ /**
84
+ * Parse a CANT message using WASM
85
+ */
86
+ function cantParseWASM(content) {
87
+ if (!wasmModule) {
88
+ throw new Error('WASM not initialized. Call initWasm() first.');
89
+ }
90
+ return wasmModule.cant_parse(content);
91
+ }
92
+ /**
93
+ * Classify a directive using WASM
94
+ */
95
+ function cantClassifyDirectiveWASM(verb) {
96
+ if (!wasmModule) {
97
+ throw new Error('WASM not initialized. Call initWasm() first.');
98
+ }
99
+ return wasmModule.cant_classify_directive(verb);
100
+ }
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@cleocode/cant",
3
+ "version": "2026.3.76",
4
+ "description": "CANT protocol parser and interpreter for CLEO - wraps cant-core WASM",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist/",
9
+ "wasm/"
10
+ ],
11
+ "dependencies": {
12
+ "@cleocode/contracts": "2026.4.0",
13
+ "@cleocode/lafs": "2026.4.0"
14
+ },
15
+ "devDependencies": {
16
+ "typescript": "^5.0.0",
17
+ "vitest": "^1.0.0"
18
+ },
19
+ "keywords": [
20
+ "cleo",
21
+ "cant",
22
+ "agent",
23
+ "parser",
24
+ "wasm"
25
+ ],
26
+ "license": "MIT",
27
+ "scripts": {
28
+ "build": "tsc",
29
+ "build:wasm": "cd ../../crates/cant-core && wasm-pack build --target web --features wasm --out-dir ../../packages/cant/wasm",
30
+ "test": "vitest run",
31
+ "test:watch": "vitest"
32
+ }
33
+ }