@cleocode/cant 2026.4.15 → 2026.4.16
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/composer.d.ts +53 -0
- package/dist/context-provider-brain.d.ts +47 -0
- package/dist/context-provider-brain.js +203 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +4 -1
- package/dist/worktree.d.ts +21 -1
- package/dist/worktree.js +8 -2
- package/package.json +4 -3
package/dist/composer.d.ts
CHANGED
|
@@ -102,6 +102,50 @@ export interface SpawnPayload {
|
|
|
102
102
|
/** Whether mental model was injected. */
|
|
103
103
|
mentalModelInjected: boolean;
|
|
104
104
|
}
|
|
105
|
+
/**
|
|
106
|
+
* Path-scoped file permissions for a CANT agent (T423 — ULTRAPLAN §9.2).
|
|
107
|
+
*
|
|
108
|
+
* Mirrors the Rust `PathPermissions` struct in `crates/cant-core/src/dsl/ast.rs`.
|
|
109
|
+
* Enforced at runtime by the `tool_call` hook in `cleo-cant-bridge.ts`.
|
|
110
|
+
*
|
|
111
|
+
* Security rules:
|
|
112
|
+
* - An **empty `write` array means no writes are allowed** (default-deny).
|
|
113
|
+
* - An absent field (`undefined`) means that access level is unrestricted.
|
|
114
|
+
*
|
|
115
|
+
* @example
|
|
116
|
+
* ```cant
|
|
117
|
+
* agent backend-dev:
|
|
118
|
+
* permissions:
|
|
119
|
+
* files:
|
|
120
|
+
* write: ["packages/cleo/**", "crates/**"]
|
|
121
|
+
* read: ["**\/*"]
|
|
122
|
+
* delete: ["packages/cleo/**"]
|
|
123
|
+
* ```
|
|
124
|
+
*
|
|
125
|
+
* @task T423
|
|
126
|
+
*/
|
|
127
|
+
export interface PathPermissions {
|
|
128
|
+
/**
|
|
129
|
+
* Glob patterns for paths this agent may write (Edit/Write tool).
|
|
130
|
+
*
|
|
131
|
+
* An empty array means no writes are allowed (read-only agent).
|
|
132
|
+
* `undefined` means no write ACL was declared (unrestricted).
|
|
133
|
+
*/
|
|
134
|
+
write?: string[];
|
|
135
|
+
/**
|
|
136
|
+
* Glob patterns for paths this agent may read.
|
|
137
|
+
*
|
|
138
|
+
* `undefined` means reads are unrestricted (default-allow).
|
|
139
|
+
*/
|
|
140
|
+
read?: string[];
|
|
141
|
+
/**
|
|
142
|
+
* Glob patterns for paths this agent may delete.
|
|
143
|
+
*
|
|
144
|
+
* An empty array means no deletes are allowed.
|
|
145
|
+
* `undefined` means deletes are unrestricted.
|
|
146
|
+
*/
|
|
147
|
+
delete?: string[];
|
|
148
|
+
}
|
|
105
149
|
/** Agent definition extracted from a compiled `.cant` bundle. */
|
|
106
150
|
export interface AgentDefinition {
|
|
107
151
|
/** The agent name as declared in the `.cant` file. */
|
|
@@ -143,6 +187,15 @@ export interface AgentDefinition {
|
|
|
143
187
|
} | null;
|
|
144
188
|
/** Behavior when total tokens exceed the tier cap. */
|
|
145
189
|
onOverflow: 'escalate_tier' | 'fail';
|
|
190
|
+
/**
|
|
191
|
+
* Path-scoped file permissions declared via `permissions: files:` in the `.cant` file.
|
|
192
|
+
*
|
|
193
|
+
* `undefined` means no path ACL was declared (tool-level enforcement only).
|
|
194
|
+
* When present, the `tool_call` hook enforces write/delete path restrictions.
|
|
195
|
+
*
|
|
196
|
+
* @task T423
|
|
197
|
+
*/
|
|
198
|
+
filePermissions?: PathPermissions;
|
|
146
199
|
}
|
|
147
200
|
/**
|
|
148
201
|
* Compose a spawn payload for an agent.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BRAIN-backed ContextProvider implementation for the JIT Agent Composer.
|
|
3
|
+
*
|
|
4
|
+
* Wires the `ContextProvider` interface from `composer.ts` to the live BRAIN
|
|
5
|
+
* database via `memoryFind` from `@cleocode/core`. This is the critical T432
|
|
6
|
+
* activation — without this provider, `composeSpawnPayload` cannot pull real
|
|
7
|
+
* context from BRAIN at spawn time.
|
|
8
|
+
*
|
|
9
|
+
* The `agent` filter (T417/T418) ensures that mental model observations written
|
|
10
|
+
* by a specific agent are returned rather than unrelated global observations.
|
|
11
|
+
*
|
|
12
|
+
* @epic T377
|
|
13
|
+
* @task T432
|
|
14
|
+
* @packageDocumentation
|
|
15
|
+
*/
|
|
16
|
+
import type { ContextProvider } from './composer.js';
|
|
17
|
+
/**
|
|
18
|
+
* A {@link ContextProvider} implementation that retrieves context from the
|
|
19
|
+
* BRAIN database (`brain.db`) via the `memoryFind` and `memoryFetch` functions
|
|
20
|
+
* from `@cleocode/core`.
|
|
21
|
+
*
|
|
22
|
+
* @remarks
|
|
23
|
+
* This provider is designed for use in the canonical spawn path:
|
|
24
|
+
* `composeSpawnPayload(agentDef, brainContextProvider(projectRoot), projectHash)`.
|
|
25
|
+
*
|
|
26
|
+
* The `source` parameter maps to BRAIN table categories:
|
|
27
|
+
* - `"patterns"` → searches the patterns table
|
|
28
|
+
* - `"decisions"` → searches the decisions table
|
|
29
|
+
* - `"learnings"` → searches the learnings table
|
|
30
|
+
* - `"observations"` → searches the observations table (default)
|
|
31
|
+
*
|
|
32
|
+
* @param projectRoot - Absolute path to the project root (where `.cleo/` lives).
|
|
33
|
+
* @returns A `ContextProvider` instance backed by the project's BRAIN database.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```typescript
|
|
37
|
+
* import { composeSpawnPayload } from '@cleocode/cant';
|
|
38
|
+
* import { brainContextProvider } from '@cleocode/cant/context-provider-brain';
|
|
39
|
+
*
|
|
40
|
+
* const payload = await composeSpawnPayload(
|
|
41
|
+
* agentDef,
|
|
42
|
+
* brainContextProvider('/project/root'),
|
|
43
|
+
* projectHash,
|
|
44
|
+
* );
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export declare function brainContextProvider(projectRoot: string): ContextProvider;
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* BRAIN-backed ContextProvider implementation for the JIT Agent Composer.
|
|
4
|
+
*
|
|
5
|
+
* Wires the `ContextProvider` interface from `composer.ts` to the live BRAIN
|
|
6
|
+
* database via `memoryFind` from `@cleocode/core`. This is the critical T432
|
|
7
|
+
* activation — without this provider, `composeSpawnPayload` cannot pull real
|
|
8
|
+
* context from BRAIN at spawn time.
|
|
9
|
+
*
|
|
10
|
+
* The `agent` filter (T417/T418) ensures that mental model observations written
|
|
11
|
+
* by a specific agent are returned rather than unrelated global observations.
|
|
12
|
+
*
|
|
13
|
+
* @epic T377
|
|
14
|
+
* @task T432
|
|
15
|
+
* @packageDocumentation
|
|
16
|
+
*/
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.brainContextProvider = brainContextProvider;
|
|
19
|
+
const composer_js_1 = require("./composer.js");
|
|
20
|
+
/** Lazy-load memoryFind and memoryFetch from @cleocode/core/internal. */
|
|
21
|
+
async function loadBrainOps() {
|
|
22
|
+
// Variable path prevents tsc from resolving the module at type-check time.
|
|
23
|
+
const modPath = '@cleocode/core/internal';
|
|
24
|
+
const mod = await import(/* webpackIgnore: true */ modPath);
|
|
25
|
+
return { memoryFind: mod.memoryFind, memoryFetch: mod.memoryFetch };
|
|
26
|
+
}
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
// Internal helpers
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
/**
|
|
31
|
+
* Truncate text to fit within a token budget using a 4 chars/token estimate.
|
|
32
|
+
*
|
|
33
|
+
* @param text - The text to truncate.
|
|
34
|
+
* @param maxTokens - The maximum token count.
|
|
35
|
+
* @returns The (possibly truncated) text.
|
|
36
|
+
*/
|
|
37
|
+
function truncateToTokenBudget(text, maxTokens) {
|
|
38
|
+
if (maxTokens <= 0)
|
|
39
|
+
return '';
|
|
40
|
+
const maxChars = maxTokens * 4;
|
|
41
|
+
if (text.length <= maxChars)
|
|
42
|
+
return text;
|
|
43
|
+
return text.slice(0, maxChars);
|
|
44
|
+
}
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
// BRAIN ContextProvider
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
/**
|
|
49
|
+
* A {@link ContextProvider} implementation that retrieves context from the
|
|
50
|
+
* BRAIN database (`brain.db`) via the `memoryFind` and `memoryFetch` functions
|
|
51
|
+
* from `@cleocode/core`.
|
|
52
|
+
*
|
|
53
|
+
* @remarks
|
|
54
|
+
* This provider is designed for use in the canonical spawn path:
|
|
55
|
+
* `composeSpawnPayload(agentDef, brainContextProvider(projectRoot), projectHash)`.
|
|
56
|
+
*
|
|
57
|
+
* The `source` parameter maps to BRAIN table categories:
|
|
58
|
+
* - `"patterns"` → searches the patterns table
|
|
59
|
+
* - `"decisions"` → searches the decisions table
|
|
60
|
+
* - `"learnings"` → searches the learnings table
|
|
61
|
+
* - `"observations"` → searches the observations table (default)
|
|
62
|
+
*
|
|
63
|
+
* @param projectRoot - Absolute path to the project root (where `.cleo/` lives).
|
|
64
|
+
* @returns A `ContextProvider` instance backed by the project's BRAIN database.
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```typescript
|
|
68
|
+
* import { composeSpawnPayload } from '@cleocode/cant';
|
|
69
|
+
* import { brainContextProvider } from '@cleocode/cant/context-provider-brain';
|
|
70
|
+
*
|
|
71
|
+
* const payload = await composeSpawnPayload(
|
|
72
|
+
* agentDef,
|
|
73
|
+
* brainContextProvider('/project/root'),
|
|
74
|
+
* projectHash,
|
|
75
|
+
* );
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
function brainContextProvider(projectRoot) {
|
|
79
|
+
return {
|
|
80
|
+
/**
|
|
81
|
+
* Query BRAIN for context entries matching a source category and query string.
|
|
82
|
+
*
|
|
83
|
+
* @param source - BRAIN table category (patterns, decisions, learnings, observations).
|
|
84
|
+
* @param query - The search query string.
|
|
85
|
+
* @param maxTokens - Maximum token budget for the returned content.
|
|
86
|
+
* @returns A ContextSlice with content truncated to the token budget.
|
|
87
|
+
*/
|
|
88
|
+
async queryContext(source, query, maxTokens) {
|
|
89
|
+
if (maxTokens <= 0) {
|
|
90
|
+
return { source, content: '', tokens: 0 };
|
|
91
|
+
}
|
|
92
|
+
try {
|
|
93
|
+
// Map source to a BRAIN table filter.
|
|
94
|
+
const validTables = ['patterns', 'decisions', 'learnings', 'observations'];
|
|
95
|
+
const table = validTables.includes(source)
|
|
96
|
+
? [source]
|
|
97
|
+
: undefined;
|
|
98
|
+
const { memoryFind, memoryFetch } = await loadBrainOps();
|
|
99
|
+
const findResult = await memoryFind({
|
|
100
|
+
query,
|
|
101
|
+
limit: 10,
|
|
102
|
+
tables: table,
|
|
103
|
+
}, projectRoot);
|
|
104
|
+
if (!findResult.success || !findResult.data) {
|
|
105
|
+
return { source, content: '', tokens: 0 };
|
|
106
|
+
}
|
|
107
|
+
// memoryFind returns { results: BrainCompactHit[] }
|
|
108
|
+
const data = findResult.data;
|
|
109
|
+
const hits = data.results ?? [];
|
|
110
|
+
if (hits.length === 0) {
|
|
111
|
+
return { source, content: '', tokens: 0 };
|
|
112
|
+
}
|
|
113
|
+
// Fetch full content for the top hits within the budget.
|
|
114
|
+
const ids = hits.map((h) => h.id);
|
|
115
|
+
const fetchResult = await memoryFetch({ ids }, projectRoot);
|
|
116
|
+
let content = '';
|
|
117
|
+
if (fetchResult.success && fetchResult.data) {
|
|
118
|
+
const fetchData = fetchResult.data;
|
|
119
|
+
const entries = fetchData.entries ?? [];
|
|
120
|
+
content = entries.map((e) => `[${e.title}]\n${e.content ?? e.text ?? ''}`).join('\n\n');
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
// Fallback to compact titles if fetch failed.
|
|
124
|
+
content = hits.map((h) => h.title).join('\n');
|
|
125
|
+
}
|
|
126
|
+
const truncated = truncateToTokenBudget(content, maxTokens);
|
|
127
|
+
return {
|
|
128
|
+
source,
|
|
129
|
+
content: truncated,
|
|
130
|
+
tokens: (0, composer_js_1.estimateTokens)(truncated),
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
// Return empty slice on any error — the composer handles missing context gracefully.
|
|
135
|
+
return { source, content: '', tokens: 0 };
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
/**
|
|
139
|
+
* Load the mental model for an agent from BRAIN observations.
|
|
140
|
+
*
|
|
141
|
+
* Uses the T417/T418 `agent` filter to retrieve only observations
|
|
142
|
+
* produced by the specific agent for this project scope.
|
|
143
|
+
*
|
|
144
|
+
* @param agentName - The agent name to load the mental model for.
|
|
145
|
+
* @param projectHash - Project identifier ('global' for global scope).
|
|
146
|
+
* @param maxTokens - Maximum token budget for the mental model content.
|
|
147
|
+
* @returns A MentalModelSlice with content truncated to the token budget.
|
|
148
|
+
*/
|
|
149
|
+
async loadMentalModel(agentName, projectHash, maxTokens) {
|
|
150
|
+
if (maxTokens <= 0) {
|
|
151
|
+
return { content: '', tokens: 0, lastConsolidated: null };
|
|
152
|
+
}
|
|
153
|
+
try {
|
|
154
|
+
const { memoryFind, memoryFetch } = await loadBrainOps();
|
|
155
|
+
// Query brain_observations filtered by agent name (T417/T418).
|
|
156
|
+
const findResult = await memoryFind({
|
|
157
|
+
query: `mental model ${agentName}`,
|
|
158
|
+
limit: 5,
|
|
159
|
+
tables: ['observations'],
|
|
160
|
+
agent: agentName,
|
|
161
|
+
}, projectRoot);
|
|
162
|
+
if (!findResult.success || !findResult.data) {
|
|
163
|
+
return { content: '', tokens: 0, lastConsolidated: null };
|
|
164
|
+
}
|
|
165
|
+
const data = findResult.data;
|
|
166
|
+
const hits = data.results ?? [];
|
|
167
|
+
if (hits.length === 0) {
|
|
168
|
+
return { content: '', tokens: 0, lastConsolidated: null };
|
|
169
|
+
}
|
|
170
|
+
// Sort by date descending and take the most recent entries.
|
|
171
|
+
const sorted = [...hits].sort((a, b) => (b.date ?? '').localeCompare(a.date ?? ''));
|
|
172
|
+
const ids = sorted.map((h) => h.id);
|
|
173
|
+
const fetchResult = await memoryFetch({ ids }, projectRoot);
|
|
174
|
+
let content = '';
|
|
175
|
+
let lastConsolidated = null;
|
|
176
|
+
if (fetchResult.success && fetchResult.data) {
|
|
177
|
+
const fetchData = fetchResult.data;
|
|
178
|
+
const entries = fetchData.entries ?? [];
|
|
179
|
+
content = entries.map((e) => `[${e.title}]\n${e.content ?? e.text ?? ''}`).join('\n\n');
|
|
180
|
+
lastConsolidated = entries[0]?.date ?? null;
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
content = sorted.map((h) => h.title).join('\n');
|
|
184
|
+
lastConsolidated = sorted[0]?.date ?? null;
|
|
185
|
+
}
|
|
186
|
+
// Scope prefix to help the agent understand its mental model context.
|
|
187
|
+
const prefix = projectHash === 'global'
|
|
188
|
+
? `[Global mental model for ${agentName}]\n`
|
|
189
|
+
: `[Project mental model for ${agentName} — scope: ${projectHash}]\n`;
|
|
190
|
+
const full = prefix + content;
|
|
191
|
+
const truncated = truncateToTokenBudget(full, maxTokens);
|
|
192
|
+
return {
|
|
193
|
+
content: truncated,
|
|
194
|
+
tokens: (0, composer_js_1.estimateTokens)(truncated),
|
|
195
|
+
lastConsolidated,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
catch {
|
|
199
|
+
return { content: '', tokens: 0, lastConsolidated: null };
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
};
|
|
203
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
export type { LAFSEnvelope, LAFSError, LAFSMeta, MVILevel } from '@cleocode/lafs';
|
|
2
2
|
export type { AgentEntry, BundleDiagnostic, CompiledBundle, ParsedCantDocument, TeamEntry, ToolEntry, } from './bundle';
|
|
3
3
|
export { compileBundle } from './bundle';
|
|
4
|
-
export type { AgentDefinition, ContextProvider, ContextSlice, MentalModelSlice, SpawnPayload, Tier, } from './composer.js';
|
|
4
|
+
export type { AgentDefinition, ContextProvider, ContextSlice, MentalModelSlice, PathPermissions, SpawnPayload, Tier, } from './composer.js';
|
|
5
5
|
export { composeSpawnPayload, escalateTier, estimateTokens, TIER_CAPS } from './composer.js';
|
|
6
|
+
export { brainContextProvider } from './context-provider-brain.js';
|
|
6
7
|
export type { CantDocumentResult, CantListResult, CantPipelineResult, CantValidationResult, SectionKind, } from './document';
|
|
7
8
|
export { executePipeline, listSections, parseDocument, validateDocument, } from './document';
|
|
8
9
|
export type { Role, SpawnValidation, TeamDefinition, TeamRouting } from './hierarchy.js';
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
// JIT Agent Composer (ULTRAPLAN Wave 5)
|
|
3
|
+
// Wave 7a: BRAIN-backed ContextProvider (T432)
|
|
3
4
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.resolveWorktreeRoot = exports.mergeWorktree = exports.listWorktrees = exports.createWorktree = exports.parseCANTMessage = exports.initCantParser = exports.isWasmAvailable = exports.isNativeAvailable = exports.initWasm = exports.cantValidateDocumentNative = exports.cantParseNative = exports.cantParseDocumentNative = exports.cantExtractAgentProfilesNative = exports.cantExecutePipelineNative = exports.cantClassifyDirectiveNative = exports.showSummary = exports.showDiff = exports.serializeCantDocument = exports.migrateMarkdown = exports.renderMentalModel = exports.harvestObservations = exports.createEmptyModel = exports.consolidate = exports.validateSpawnRequest = exports.ORCHESTRATOR_FORBIDDEN_TOOLS = exports.LEAD_FORBIDDEN_TOOLS = exports.filterToolsForRole = exports.validateDocument = exports.parseDocument = exports.listSections = exports.executePipeline = exports.TIER_CAPS = exports.estimateTokens = exports.escalateTier = exports.composeSpawnPayload = exports.compileBundle = void 0;
|
|
5
|
+
exports.resolveWorktreeRoot = exports.mergeWorktree = exports.listWorktrees = exports.createWorktree = exports.parseCANTMessage = exports.initCantParser = exports.isWasmAvailable = exports.isNativeAvailable = exports.initWasm = exports.cantValidateDocumentNative = exports.cantParseNative = exports.cantParseDocumentNative = exports.cantExtractAgentProfilesNative = exports.cantExecutePipelineNative = exports.cantClassifyDirectiveNative = exports.showSummary = exports.showDiff = exports.serializeCantDocument = exports.migrateMarkdown = exports.renderMentalModel = exports.harvestObservations = exports.createEmptyModel = exports.consolidate = exports.validateSpawnRequest = exports.ORCHESTRATOR_FORBIDDEN_TOOLS = exports.LEAD_FORBIDDEN_TOOLS = exports.filterToolsForRole = exports.validateDocument = exports.parseDocument = exports.listSections = exports.executePipeline = exports.brainContextProvider = exports.TIER_CAPS = exports.estimateTokens = exports.escalateTier = exports.composeSpawnPayload = exports.compileBundle = void 0;
|
|
5
6
|
// Bundle compiler
|
|
6
7
|
var bundle_1 = require("./bundle");
|
|
7
8
|
Object.defineProperty(exports, "compileBundle", { enumerable: true, get: function () { return bundle_1.compileBundle; } });
|
|
@@ -10,6 +11,8 @@ Object.defineProperty(exports, "composeSpawnPayload", { enumerable: true, get: f
|
|
|
10
11
|
Object.defineProperty(exports, "escalateTier", { enumerable: true, get: function () { return composer_js_1.escalateTier; } });
|
|
11
12
|
Object.defineProperty(exports, "estimateTokens", { enumerable: true, get: function () { return composer_js_1.estimateTokens; } });
|
|
12
13
|
Object.defineProperty(exports, "TIER_CAPS", { enumerable: true, get: function () { return composer_js_1.TIER_CAPS; } });
|
|
14
|
+
var context_provider_brain_js_1 = require("./context-provider-brain.js");
|
|
15
|
+
Object.defineProperty(exports, "brainContextProvider", { enumerable: true, get: function () { return context_provider_brain_js_1.brainContextProvider; } });
|
|
13
16
|
// High-level API (replaces standalone cant-cli binary)
|
|
14
17
|
var document_1 = require("./document");
|
|
15
18
|
Object.defineProperty(exports, "executePipeline", { enumerable: true, get: function () { return document_1.executePipeline; } });
|
package/dist/worktree.d.ts
CHANGED
|
@@ -18,7 +18,16 @@ export interface WorktreeRequest {
|
|
|
18
18
|
/** Why this worktree is being created. */
|
|
19
19
|
reason: 'subagent' | 'experiment' | 'parallel-wave';
|
|
20
20
|
}
|
|
21
|
-
/**
|
|
21
|
+
/**
|
|
22
|
+
* Handle returned after worktree creation; used for merge, env-var binding, and cleanup.
|
|
23
|
+
*
|
|
24
|
+
* @remarks
|
|
25
|
+
* The `projectHash` field was added in T380/ADR-041 so callers can populate
|
|
26
|
+
* `CLEO_PROJECT_HASH` in spawned subagent environments without threading
|
|
27
|
+
* {@link WorktreeConfig} through every call site.
|
|
28
|
+
*
|
|
29
|
+
* @task T380
|
|
30
|
+
*/
|
|
22
31
|
export interface WorktreeHandle {
|
|
23
32
|
/** Absolute path to the worktree directory. */
|
|
24
33
|
path: string;
|
|
@@ -28,6 +37,17 @@ export interface WorktreeHandle {
|
|
|
28
37
|
baseRef: string;
|
|
29
38
|
/** Task ID. */
|
|
30
39
|
taskId: string;
|
|
40
|
+
/**
|
|
41
|
+
* Project hash used to scope this worktree under the XDG worktree root.
|
|
42
|
+
*
|
|
43
|
+
* @remarks
|
|
44
|
+
* Sourced from {@link WorktreeConfig.projectHash} at creation time.
|
|
45
|
+
* Exposed here so spawn adapters can populate the `CLEO_PROJECT_HASH`
|
|
46
|
+
* environment variable without re-threading the full config.
|
|
47
|
+
*
|
|
48
|
+
* @task T380
|
|
49
|
+
*/
|
|
50
|
+
projectHash: string;
|
|
31
51
|
/** Clean up: remove the worktree and optionally delete the branch. */
|
|
32
52
|
cleanup(deleteBranch?: boolean): void;
|
|
33
53
|
}
|
package/dist/worktree.js
CHANGED
|
@@ -71,7 +71,7 @@ function createWorktree(request, config) {
|
|
|
71
71
|
cwd: config.gitRoot,
|
|
72
72
|
stdio: 'pipe',
|
|
73
73
|
});
|
|
74
|
-
return buildHandle(worktreePath, branch, request.baseRef, request.taskId, config.gitRoot);
|
|
74
|
+
return buildHandle(worktreePath, branch, request.baseRef, request.taskId, config.gitRoot, config.projectHash);
|
|
75
75
|
}
|
|
76
76
|
/**
|
|
77
77
|
* Merge a worktree's branch back into the current branch.
|
|
@@ -148,14 +148,20 @@ function listWorktrees(config) {
|
|
|
148
148
|
/**
|
|
149
149
|
* Build a WorktreeHandle with a cleanup closure.
|
|
150
150
|
*
|
|
151
|
+
* @remarks
|
|
152
|
+
* The `projectHash` parameter was added in T380/ADR-041 and is stored on the
|
|
153
|
+
* returned handle so spawn adapters can populate `CLEO_PROJECT_HASH` without
|
|
154
|
+
* re-threading {@link WorktreeConfig}.
|
|
155
|
+
*
|
|
151
156
|
* @internal
|
|
152
157
|
*/
|
|
153
|
-
function buildHandle(worktreePath, branch, baseRef, taskId, gitRoot) {
|
|
158
|
+
function buildHandle(worktreePath, branch, baseRef, taskId, gitRoot, projectHash) {
|
|
154
159
|
return {
|
|
155
160
|
path: worktreePath,
|
|
156
161
|
branch,
|
|
157
162
|
baseRef,
|
|
158
163
|
taskId,
|
|
164
|
+
projectHash,
|
|
159
165
|
cleanup(deleteBranch = false) {
|
|
160
166
|
try {
|
|
161
167
|
(0, node_child_process_1.execSync)(`git worktree remove "${worktreePath}" --force`, {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cleocode/cant",
|
|
3
|
-
"version": "2026.4.
|
|
3
|
+
"version": "2026.4.16",
|
|
4
4
|
"description": "CANT protocol parser and runtime for CLEO — wraps cant-core via napi-rs",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -9,8 +9,9 @@
|
|
|
9
9
|
"napi/"
|
|
10
10
|
],
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@cleocode/
|
|
13
|
-
"@cleocode/
|
|
12
|
+
"@cleocode/contracts": "2026.4.16",
|
|
13
|
+
"@cleocode/core": "2026.4.16",
|
|
14
|
+
"@cleocode/lafs": "2026.4.16"
|
|
14
15
|
},
|
|
15
16
|
"devDependencies": {
|
|
16
17
|
"typescript": "^6.0.2",
|