cowork-flow 0.0.46 → 0.0.48
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/CHANGELOG.md +41 -0
- package/README.md +86 -22
- package/package.json +3 -1
- package/src/commands/init.js +0 -83
- package/src/commands/sync.js +6 -0
- package/src/commands/update.js +59 -6
- package/src/lib/copy-template.js +70 -49
- package/src/lib/host-assets.js +185 -27
- package/src/lib/plan-applier.js +47 -0
- package/template/.claude/hooks/inject-workflow-state.py +7 -0
- package/template/.codex/hooks/inject-workflow-state.py +7 -0
- package/template/.cowork-flow/.version +1 -1
- package/template/.cowork-flow/config.yaml +0 -11
- package/template/.cowork-flow/scripts/adapters/cli/init_developer.py +0 -1
- package/template/.cowork-flow/scripts/adapters/cli/subagent.py +7 -4
- package/template/.cowork-flow/scripts/adapters/cli/task.py +34 -669
- package/template/.cowork-flow/scripts/adapters/cli/task_archive_commands.py +3 -134
- package/template/.cowork-flow/scripts/adapters/cli/task_context_commands.py +7 -1
- package/template/.cowork-flow/scripts/adapters/cli/task_lifecycle_commands.py +503 -0
- package/template/.cowork-flow/scripts/adapters/cli/task_navigation.py +151 -2
- package/template/.cowork-flow/scripts/adapters/cli/task_next_runner.py +229 -0
- package/template/.cowork-flow/scripts/adapters/cli/task_parser.py +1 -1
- package/template/.cowork-flow/scripts/adapters/cli/task_tree_commands.py +68 -0
- package/template/.cowork-flow/scripts/adapters/git/git_context.py +16 -193
- package/template/.cowork-flow/scripts/adapters/host/host_manifest.py +102 -0
- package/template/.cowork-flow/scripts/infra/config.py +0 -17
- package/template/.cowork-flow/scripts/infra/developer_profile.py +0 -95
- package/template/.cowork-flow/scripts/infra/paths.py +0 -79
- package/template/.cowork-flow/scripts/infra/skill_manifest.py +25 -2
- package/template/.cowork-flow/scripts/kernel/workflow_route.py +15 -15
- package/template/.cowork-flow/scripts/run.py +0 -5
- package/template/.cowork-flow/scripts/services/lifecycle_checks.py +163 -65
- package/template/.cowork-flow/scripts/services/readiness.py +3 -170
- package/template/.cowork-flow/scripts/services/task_archive.py +16 -11
- package/template/.cowork-flow/scripts/services/task_context.py +208 -40
- package/template/.cowork-flow/scripts/services/task_creation.py +32 -1
- package/template/.cowork-flow/scripts/services/task_routing.py +80 -56
- package/template/.cowork-flow/scripts/services/task_utils.py +0 -146
- package/template/.cowork-flow/scripts/services/workflow_runtime.py +1 -1
- package/template/.cowork-flow/spec/contracts/capabilities.md +23 -0
- package/template/.cowork-flow/spec/contracts/decision-anchor.md +2 -2
- package/template/.cowork-flow/spec/contracts/index.md +1 -0
- package/template/.cowork-flow/spec/contracts/party-mode-v2-board.md +20 -0
- package/template/.cowork-flow/spec/contracts/plan-binding.md +50 -0
- package/template/.cowork-flow/spec/contracts/skill-owned-actions.md +40 -7
- package/template/.cowork-flow/spec/guides/index.md +3 -1
- package/template/.cowork-flow/spec/index.md +5 -4
- package/template/.cowork-flow/spec/references/definition-of-done.md +2 -3
- package/template/.cowork-flow/spec/references/orchestration-patterns.md +1 -1
- package/template/.cowork-flow/spec/runtime/contract-registry.json +14 -0
- package/template/.cowork-flow/spec/runtime/host-assets.json +73 -3
- package/template/.cowork-flow/spec/schemas/host-assets.schema.json +119 -3
- package/template/.zcode/.zcode-plugin/plugin.json +1 -1
- package/template/skills/adversarial-review/SKILL.md +17 -0
- package/template/skills/batch-execution/SKILL.md +37 -3
- package/template/skills/batch-execution/scripts/batch_action.py +4 -0
- package/template/skills/batch-execution/scripts/batch_execution.py +154 -15
- package/template/skills/brainstorming/SKILL.md +8 -7
- package/template/skills/party-mode/scripts/party_action_contract.py +113 -0
- package/template/skills/party-mode/scripts/party_board_store.py +148 -0
- package/template/skills/party-mode/scripts/party_mode_v2.py +246 -142
- package/template/skills/runtime-health/SKILL.md +18 -1
- package/template/skills/runtime-health/scripts/doctor.py +225 -11
- package/template/skills/task-planning/SKILL.md +105 -61
- package/template/skills/task-review/SKILL.md +9 -4
- package/template/skills/task-review/manifest.json +4 -2
- package/template/skills/task-review/scripts/review_check.py +142 -18
- package/template/.cowork-flow/changes/archive/.gitkeep +0 -1
- package/template/.cowork-flow/scripts/adapters/cli/add_session.py +0 -465
- package/template/.cowork-flow/scripts/adapters/cli/change.py +0 -412
- package/template/.cowork-flow/workspace/index.md +0 -102
- package/template/.zcode/scaffold/AGENTS.md +0 -145
- package/template/.zcode/scaffold/CLAUDE.md +0 -5
package/src/lib/host-assets.js
CHANGED
|
@@ -12,6 +12,19 @@ export const hostAssetManifestPath = join(
|
|
|
12
12
|
'host-assets.json'
|
|
13
13
|
);
|
|
14
14
|
|
|
15
|
+
const REQUIRED_HOST_NEUTRAL_CAPABILITIES = [
|
|
16
|
+
'task_action',
|
|
17
|
+
'subagent_dispatch',
|
|
18
|
+
'file_write',
|
|
19
|
+
'party_board_action'
|
|
20
|
+
];
|
|
21
|
+
const REQUIRED_CAPABILITY_MATRIX_HOSTS = [
|
|
22
|
+
'codex',
|
|
23
|
+
'claude-code',
|
|
24
|
+
'opencode',
|
|
25
|
+
'zcode'
|
|
26
|
+
];
|
|
27
|
+
|
|
15
28
|
|
|
16
29
|
export function loadHostAssetManifest(path = hostAssetManifestPath) {
|
|
17
30
|
const manifest = JSON.parse(readFileSync(path, 'utf8'));
|
|
@@ -41,6 +54,7 @@ export function createHostRegistry(manifest) {
|
|
|
41
54
|
}
|
|
42
55
|
}
|
|
43
56
|
const platformIds = platforms.map((platform) => platform.id);
|
|
57
|
+
const capabilityMatrix = normalizeCapabilityMatrix(manifest.capabilityMatrix);
|
|
44
58
|
const syncPolicy = {
|
|
45
59
|
protectedFiles: manifest.syncPolicy.protectedFiles.map(normalizePath),
|
|
46
60
|
protectedPrefixes: manifest.syncPolicy.protectedPrefixes.map(normalizePath),
|
|
@@ -49,6 +63,20 @@ export function createHostRegistry(manifest) {
|
|
|
49
63
|
managedBlockFiles: manifest.syncPolicy.managedBlockFiles.map(normalizePath),
|
|
50
64
|
obsoleteFiles: manifest.syncPolicy.obsoleteFiles.map(normalizePath)
|
|
51
65
|
};
|
|
66
|
+
const assetPrefixes = unique(
|
|
67
|
+
platforms.flatMap((platform) => platform.assetPrefixes)
|
|
68
|
+
);
|
|
69
|
+
const skillTargets = unique(
|
|
70
|
+
platforms
|
|
71
|
+
.map((platform) => platform.skillTarget)
|
|
72
|
+
.filter(
|
|
73
|
+
(value) => typeof value === 'string' && value.length > 0
|
|
74
|
+
)
|
|
75
|
+
.map(normalizePath)
|
|
76
|
+
);
|
|
77
|
+
const protectedFiles = new Set(syncPolicy.protectedFiles);
|
|
78
|
+
const safeFiles = new Set(syncPolicy.safeFiles);
|
|
79
|
+
const managedBlockFiles = new Set(syncPolicy.managedBlockFiles);
|
|
52
80
|
|
|
53
81
|
function parsePlatformSelection(values) {
|
|
54
82
|
const rawValues = Array.isArray(values) ? values : [values];
|
|
@@ -92,20 +120,57 @@ export function createHostRegistry(manifest) {
|
|
|
92
120
|
) {
|
|
93
121
|
return false;
|
|
94
122
|
}
|
|
95
|
-
const owners =
|
|
96
|
-
platform.assetFiles.includes(normalized)
|
|
97
|
-
|| platform.assetPrefixes.some(
|
|
98
|
-
(prefix) => normalized.startsWith(prefix)
|
|
99
|
-
)
|
|
100
|
-
));
|
|
123
|
+
const owners = assetOwners(normalized);
|
|
101
124
|
if (owners.length === 0) {
|
|
102
125
|
return true;
|
|
103
126
|
}
|
|
104
127
|
return owners.some(
|
|
105
|
-
(
|
|
128
|
+
(platformId) => selectedPlatforms.includes(platformId)
|
|
106
129
|
);
|
|
107
130
|
}
|
|
108
131
|
|
|
132
|
+
function assetOwners(relativePath) {
|
|
133
|
+
const normalized = normalizePath(relativePath);
|
|
134
|
+
return platforms
|
|
135
|
+
.filter((platform) => ownsAsset(platform, normalized))
|
|
136
|
+
.map((platform) => platform.id);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function isKnownPlatformAsset(relativePath) {
|
|
140
|
+
return assetOwners(relativePath).length > 0;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function isSafeSyncFile(relativePath) {
|
|
144
|
+
const normalized = normalizePath(relativePath);
|
|
145
|
+
return safeFiles.has(normalized)
|
|
146
|
+
|| syncPolicy.safePrefixes.some((prefix) => normalized.startsWith(prefix))
|
|
147
|
+
|| assetPrefixes.some((prefix) => normalized.startsWith(prefix))
|
|
148
|
+
|| skillTargets.some((target) => normalized.startsWith(`${target}/`))
|
|
149
|
+
|| normalized.endsWith('/.gitkeep');
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function isProtectedSyncFile(relativePath) {
|
|
153
|
+
const normalized = normalizePath(relativePath);
|
|
154
|
+
if (safeFiles.has(normalized)) {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
return protectedFiles.has(normalized)
|
|
158
|
+
|| syncPolicy.protectedPrefixes.some((prefix) => normalized.startsWith(prefix));
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function isManagedBlockFile(relativePath) {
|
|
162
|
+
return managedBlockFiles.has(normalizePath(relativePath));
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function obsoleteSyncFiles() {
|
|
166
|
+
return [...syncPolicy.obsoleteFiles].sort((left, right) => {
|
|
167
|
+
if (left.length !== right.length) {
|
|
168
|
+
return left.length - right.length;
|
|
169
|
+
}
|
|
170
|
+
return left.localeCompare(right);
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
109
174
|
async function detectInstalledPlatforms(targetDir, pathExists) {
|
|
110
175
|
const installed = [];
|
|
111
176
|
for (const platform of platforms) {
|
|
@@ -123,20 +188,13 @@ export function createHostRegistry(manifest) {
|
|
|
123
188
|
manifest,
|
|
124
189
|
platforms,
|
|
125
190
|
platformIds,
|
|
191
|
+
capabilityMatrix,
|
|
126
192
|
syncPolicy,
|
|
127
|
-
assetPrefixes
|
|
128
|
-
|
|
129
|
-
),
|
|
130
|
-
skillTargets: unique(
|
|
131
|
-
platforms
|
|
132
|
-
.map((platform) => platform.skillTarget)
|
|
133
|
-
.filter(
|
|
134
|
-
(value) => typeof value === 'string' && value.length > 0
|
|
135
|
-
)
|
|
136
|
-
.map(normalizePath)
|
|
137
|
-
),
|
|
193
|
+
assetPrefixes,
|
|
194
|
+
skillTargets,
|
|
138
195
|
parsePlatformSelection,
|
|
139
196
|
shouldInclude,
|
|
197
|
+
assetOwners,
|
|
140
198
|
detectInstalledPlatforms,
|
|
141
199
|
platform(platformId) {
|
|
142
200
|
return byId.get(platformId) ?? null;
|
|
@@ -144,22 +202,29 @@ export function createHostRegistry(manifest) {
|
|
|
144
202
|
platformLabel(platformId) {
|
|
145
203
|
return byId.get(platformId)?.displayName ?? platformId;
|
|
146
204
|
},
|
|
205
|
+
hostCapability(hostId, capability) {
|
|
206
|
+
return capabilityMatrix.hosts[hostId]?.[capability] ?? null;
|
|
207
|
+
},
|
|
147
208
|
skillDestination(platformId) {
|
|
148
209
|
return byId.get(platformId)?.skillTarget ?? null;
|
|
149
210
|
},
|
|
150
|
-
isKnownPlatformAsset
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
(prefix) => normalized.startsWith(prefix)
|
|
156
|
-
)
|
|
157
|
-
));
|
|
158
|
-
}
|
|
211
|
+
isKnownPlatformAsset,
|
|
212
|
+
isSafeSyncFile,
|
|
213
|
+
isProtectedSyncFile,
|
|
214
|
+
isManagedBlockFile,
|
|
215
|
+
obsoleteSyncFiles
|
|
159
216
|
};
|
|
160
217
|
}
|
|
161
218
|
|
|
162
219
|
|
|
220
|
+
function ownsAsset(platform, normalizedPath) {
|
|
221
|
+
return platform.assetFiles.includes(normalizedPath)
|
|
222
|
+
|| platform.assetPrefixes.some(
|
|
223
|
+
(prefix) => normalizedPath.startsWith(prefix)
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
|
|
163
228
|
function validateManifest(manifest) {
|
|
164
229
|
if (!manifest || typeof manifest !== 'object' || Array.isArray(manifest)) {
|
|
165
230
|
throw new Error('Host Asset Manifest must be an object');
|
|
@@ -179,6 +244,7 @@ function validateManifest(manifest) {
|
|
|
179
244
|
'Host Asset Manifest excludedPrefixes must be an array'
|
|
180
245
|
);
|
|
181
246
|
}
|
|
247
|
+
validateCapabilityMatrix(manifest);
|
|
182
248
|
if (!manifest.syncPolicy || typeof manifest.syncPolicy !== 'object') {
|
|
183
249
|
throw new Error('Host Asset Manifest syncPolicy must be an object');
|
|
184
250
|
}
|
|
@@ -217,6 +283,91 @@ function validateManifest(manifest) {
|
|
|
217
283
|
}
|
|
218
284
|
|
|
219
285
|
|
|
286
|
+
function validateCapabilityMatrix(manifest) {
|
|
287
|
+
if (!Array.isArray(manifest.capabilityValues) || manifest.capabilityValues.length === 0) {
|
|
288
|
+
throw new Error('Host Asset Manifest capabilityValues must be a non-empty array');
|
|
289
|
+
}
|
|
290
|
+
const allowed = new Set(manifest.capabilityValues);
|
|
291
|
+
for (const value of allowed) {
|
|
292
|
+
if (typeof value !== 'string' || value.length === 0) {
|
|
293
|
+
throw new Error('Host Asset Manifest capabilityValues entries must be strings');
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
const matrix = manifest.capabilityMatrix;
|
|
297
|
+
if (!matrix || typeof matrix !== 'object' || Array.isArray(matrix)) {
|
|
298
|
+
throw new Error('Host Asset Manifest capabilityMatrix must be an object');
|
|
299
|
+
}
|
|
300
|
+
if (!arraysEqual(matrix.required, REQUIRED_HOST_NEUTRAL_CAPABILITIES)) {
|
|
301
|
+
throw new Error(
|
|
302
|
+
`Host Asset Manifest capabilityMatrix.required must be ${REQUIRED_HOST_NEUTRAL_CAPABILITIES.join(', ')}`
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
if (!matrix.hosts || typeof matrix.hosts !== 'object' || Array.isArray(matrix.hosts)) {
|
|
306
|
+
throw new Error('Host Asset Manifest capabilityMatrix.hosts must be an object');
|
|
307
|
+
}
|
|
308
|
+
const requiredHosts = new Set([
|
|
309
|
+
...REQUIRED_CAPABILITY_MATRIX_HOSTS,
|
|
310
|
+
...manifest.platforms.map((platform) => platform.id)
|
|
311
|
+
]);
|
|
312
|
+
for (const hostId of [...requiredHosts].sort()) {
|
|
313
|
+
if (!matrix.hosts[hostId]) {
|
|
314
|
+
throw new Error(`Host Asset Manifest capability matrix missing host: ${hostId}`);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
for (const [hostId, capabilities] of Object.entries(matrix.hosts)) {
|
|
318
|
+
if (!capabilities || typeof capabilities !== 'object' || Array.isArray(capabilities)) {
|
|
319
|
+
throw new Error(`Host Asset Manifest capabilityMatrix host ${hostId} must be an object`);
|
|
320
|
+
}
|
|
321
|
+
for (const key of Object.keys(capabilities)) {
|
|
322
|
+
if (!REQUIRED_HOST_NEUTRAL_CAPABILITIES.includes(key)) {
|
|
323
|
+
throw new Error(`Host Asset Manifest unknown host-neutral capability ${hostId}:${key}`);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
for (const capability of REQUIRED_HOST_NEUTRAL_CAPABILITIES) {
|
|
327
|
+
const declaration = capabilities[capability];
|
|
328
|
+
if (!declaration || typeof declaration !== 'object' || Array.isArray(declaration)) {
|
|
329
|
+
throw new Error(`Host Asset Manifest missing host-neutral capability ${hostId}:${capability}`);
|
|
330
|
+
}
|
|
331
|
+
if (!allowed.has(declaration.status)) {
|
|
332
|
+
throw new Error(
|
|
333
|
+
`Host Asset Manifest illegal host-neutral capability ${hostId}:${capability}=${declaration.status}`
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
if (declaration.fallback !== undefined && (
|
|
337
|
+
typeof declaration.fallback !== 'string' || declaration.fallback.length === 0
|
|
338
|
+
)) {
|
|
339
|
+
throw new Error(
|
|
340
|
+
`Host Asset Manifest capability fallback must be a non-empty string: ${hostId}:${capability}`
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
if (declaration.status === 'unsupported' && !declaration.fallback) {
|
|
344
|
+
throw new Error(
|
|
345
|
+
`Host Asset Manifest unsupported capability requires fallback: ${hostId}:${capability}`
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
function normalizeCapabilityMatrix(matrix) {
|
|
354
|
+
return {
|
|
355
|
+
required: [...matrix.required],
|
|
356
|
+
hosts: Object.fromEntries(
|
|
357
|
+
Object.entries(matrix.hosts).map(([hostId, capabilities]) => [
|
|
358
|
+
hostId,
|
|
359
|
+
Object.fromEntries(
|
|
360
|
+
Object.entries(capabilities).map(([capability, declaration]) => [
|
|
361
|
+
capability,
|
|
362
|
+
{ ...declaration }
|
|
363
|
+
])
|
|
364
|
+
)
|
|
365
|
+
])
|
|
366
|
+
)
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
|
|
220
371
|
function normalizePath(value) {
|
|
221
372
|
return String(value).replaceAll('\\', '/');
|
|
222
373
|
}
|
|
@@ -227,5 +378,12 @@ function unique(values) {
|
|
|
227
378
|
}
|
|
228
379
|
|
|
229
380
|
|
|
381
|
+
function arraysEqual(left, right) {
|
|
382
|
+
return Array.isArray(left)
|
|
383
|
+
&& left.length === right.length
|
|
384
|
+
&& left.every((value, index) => value === right[index]);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
|
|
230
388
|
export const hostAssetManifest = loadHostAssetManifest();
|
|
231
389
|
export const hostRegistry = createHostRegistry(hostAssetManifest);
|
package/src/lib/plan-applier.js
CHANGED
|
@@ -226,6 +226,53 @@ function validateMetadata(metadata, transactionRoot, resolvedTarget) {
|
|
|
226
226
|
}
|
|
227
227
|
}
|
|
228
228
|
|
|
229
|
+
export async function inspectAssetTransactions(
|
|
230
|
+
targetDir,
|
|
231
|
+
{ fileSystem = defaultFileSystem } = {}
|
|
232
|
+
) {
|
|
233
|
+
const fs = fileSystem ?? defaultFileSystem;
|
|
234
|
+
const resolvedTarget = resolve(targetDir);
|
|
235
|
+
const parentDir = dirname(resolvedTarget);
|
|
236
|
+
let entries;
|
|
237
|
+
try {
|
|
238
|
+
entries = await fs.readdir(parentDir, { withFileTypes: true });
|
|
239
|
+
} catch (error) {
|
|
240
|
+
if (error && error.code === 'ENOENT') {
|
|
241
|
+
return [];
|
|
242
|
+
}
|
|
243
|
+
throw error;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const prefix = transactionPrefix(resolvedTarget);
|
|
247
|
+
const transactions = [];
|
|
248
|
+
for (const entry of entries) {
|
|
249
|
+
if (!entry.isDirectory() || !entry.name.startsWith(prefix)) {
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
const transactionRoot = join(parentDir, entry.name);
|
|
253
|
+
try {
|
|
254
|
+
const metadata = await readMetadata(fs, transactionRoot);
|
|
255
|
+
validateMetadata(metadata, transactionRoot, resolvedTarget);
|
|
256
|
+
transactions.push({
|
|
257
|
+
path: transactionRoot,
|
|
258
|
+
status: metadata.status,
|
|
259
|
+
actionCount: metadata.actions.length,
|
|
260
|
+
pending: metadata.status !== 'committed'
|
|
261
|
+
});
|
|
262
|
+
} catch (error) {
|
|
263
|
+
transactions.push({
|
|
264
|
+
path: transactionRoot,
|
|
265
|
+
status: 'unknown',
|
|
266
|
+
actionCount: null,
|
|
267
|
+
pending: true,
|
|
268
|
+
error: error instanceof Error ? error.message : String(error)
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return transactions.sort((left, right) => left.path.localeCompare(right.path));
|
|
274
|
+
}
|
|
275
|
+
|
|
229
276
|
export async function recoverAssetTransactions(
|
|
230
277
|
targetDir,
|
|
231
278
|
{ fileSystem = defaultFileSystem } = {}
|
|
@@ -10,6 +10,12 @@ from pathlib import Path
|
|
|
10
10
|
from typing import Any
|
|
11
11
|
|
|
12
12
|
|
|
13
|
+
def _configure_stdio() -> None:
|
|
14
|
+
for stream in (sys.stdout, sys.stderr):
|
|
15
|
+
if hasattr(stream, "reconfigure"):
|
|
16
|
+
stream.reconfigure(encoding="utf-8", errors="replace")
|
|
17
|
+
|
|
18
|
+
|
|
13
19
|
def _read_input() -> dict[str, Any]:
|
|
14
20
|
raw = sys.stdin.buffer.read()
|
|
15
21
|
if not raw.strip():
|
|
@@ -90,4 +96,5 @@ def main() -> int:
|
|
|
90
96
|
|
|
91
97
|
|
|
92
98
|
if __name__ == "__main__":
|
|
99
|
+
_configure_stdio()
|
|
93
100
|
raise SystemExit(main())
|
|
@@ -10,6 +10,12 @@ from pathlib import Path
|
|
|
10
10
|
from typing import Any
|
|
11
11
|
|
|
12
12
|
|
|
13
|
+
def _configure_stdio() -> None:
|
|
14
|
+
for stream in (sys.stdout, sys.stderr):
|
|
15
|
+
if hasattr(stream, "reconfigure"):
|
|
16
|
+
stream.reconfigure(encoding="utf-8", errors="replace")
|
|
17
|
+
|
|
18
|
+
|
|
13
19
|
def _read_input() -> dict[str, Any]:
|
|
14
20
|
raw = sys.stdin.buffer.read()
|
|
15
21
|
if not raw.strip():
|
|
@@ -87,4 +93,5 @@ def main() -> int:
|
|
|
87
93
|
|
|
88
94
|
|
|
89
95
|
if __name__ == "__main__":
|
|
96
|
+
_configure_stdio()
|
|
90
97
|
raise SystemExit(main())
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.0.
|
|
1
|
+
0.0.48
|
|
@@ -3,17 +3,6 @@
|
|
|
3
3
|
#
|
|
4
4
|
# All values have sensible defaults. Only override what you need.
|
|
5
5
|
|
|
6
|
-
#-------------------------------------------------------------------------------
|
|
7
|
-
# Session Recording
|
|
8
|
-
#-------------------------------------------------------------------------------
|
|
9
|
-
|
|
10
|
-
# Commit message used when auto-committing journal/index changes
|
|
11
|
-
# after running add_session.py
|
|
12
|
-
session_commit_message: "chore: record journal"
|
|
13
|
-
|
|
14
|
-
# Maximum lines per journal file before rotating to a new one
|
|
15
|
-
max_journal_lines: 2000
|
|
16
|
-
|
|
17
6
|
#-------------------------------------------------------------------------------
|
|
18
7
|
# Codex Runtime
|
|
19
8
|
#-------------------------------------------------------------------------------
|
|
@@ -98,6 +98,9 @@ def _host_context_prefix(host: str) -> str:
|
|
|
98
98
|
def _suggest_host_context_key(host: str, runtime_context_id: str) -> str:
|
|
99
99
|
return f"{_host_context_prefix(host)}_{runtime_context_id}"
|
|
100
100
|
|
|
101
|
+
def _print_runtime_error(error: RuntimeContextError) -> None:
|
|
102
|
+
print(f"Error: {error.code}: {error.detail}", file=sys.stderr)
|
|
103
|
+
|
|
101
104
|
def _resolve_agent_type(role: str, agent_type: str | None) -> tuple[str, str]:
|
|
102
105
|
normalized_role = role.strip()
|
|
103
106
|
requested = agent_type.strip() if isinstance(agent_type, str) else ""
|
|
@@ -178,7 +181,7 @@ def cmd_init(args: argparse.Namespace) -> int:
|
|
|
178
181
|
context,
|
|
179
182
|
)
|
|
180
183
|
except RuntimeContextError as error:
|
|
181
|
-
|
|
184
|
+
_print_runtime_error(error)
|
|
182
185
|
return 1
|
|
183
186
|
logical_context_key = initialized.logical_context_key
|
|
184
187
|
host_context_key = _suggest_host_context_key(host, runtime_context_id)
|
|
@@ -244,7 +247,7 @@ def cmd_update(args: argparse.Namespace) -> int:
|
|
|
244
247
|
note=args.note,
|
|
245
248
|
)
|
|
246
249
|
except RuntimeContextError as error:
|
|
247
|
-
|
|
250
|
+
_print_runtime_error(error)
|
|
248
251
|
return 1
|
|
249
252
|
if context is None:
|
|
250
253
|
print(f"Error: subagent not found: {args.subagent_id}", file=sys.stderr)
|
|
@@ -261,7 +264,7 @@ def cmd_bind(args: argparse.Namespace) -> int:
|
|
|
261
264
|
args.context_key,
|
|
262
265
|
)
|
|
263
266
|
except RuntimeContextError as error:
|
|
264
|
-
|
|
267
|
+
_print_runtime_error(error)
|
|
265
268
|
return 1
|
|
266
269
|
if context is None:
|
|
267
270
|
print(f"Error: cannot bind runtime context: {args.subagent_id}", file=sys.stderr)
|
|
@@ -275,7 +278,7 @@ def cmd_close(args: argparse.Namespace) -> int:
|
|
|
275
278
|
try:
|
|
276
279
|
closed = RuntimeContextService(repo_root).close(args.subagent_id)
|
|
277
280
|
except RuntimeContextError as error:
|
|
278
|
-
|
|
281
|
+
_print_runtime_error(error)
|
|
279
282
|
return 1
|
|
280
283
|
if not closed:
|
|
281
284
|
print(f"Error: subagent not found: {args.subagent_id}", file=sys.stderr)
|