@aiwg/cli 2026.7.20 → 2026.7.21

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.
Files changed (53) hide show
  1. package/README.md +4 -4
  2. package/dist/src/api/index.d.ts +1 -0
  3. package/dist/src/api/index.js +1 -0
  4. package/dist/src/artifacts/browser-export.js +7 -0
  5. package/dist/src/artifacts/citation-parser.js +96 -35
  6. package/dist/src/artifacts/index-builder.js +54 -17
  7. package/dist/src/artifacts/state-transfer.js +27 -0
  8. package/dist/src/artifacts/stats.js +8 -0
  9. package/dist/src/cli/cli-extension-loader.js +73 -0
  10. package/dist/src/cli/handlers/index.js +3 -1
  11. package/dist/src/cli/handlers/sessions.js +966 -0
  12. package/dist/src/cli/handlers/skill-lint.js +49 -45
  13. package/dist/src/cli/handlers/use.js +143 -60
  14. package/dist/src/cli/handlers/utilities.js +22 -8
  15. package/dist/src/cli/skill-usage.js +146 -24
  16. package/dist/src/extensions/commands/definitions.js +29 -0
  17. package/dist/src/extensions/manifest.js +29 -0
  18. package/dist/src/sessions/adapters/claude.js +357 -0
  19. package/dist/src/sessions/adapters/codex.js +521 -0
  20. package/dist/src/sessions/adapters/copilot.js +226 -0
  21. package/dist/src/sessions/adapters/cursor.js +372 -0
  22. package/dist/src/sessions/adapters/factory.js +345 -0
  23. package/dist/src/sessions/adapters/generic.js +225 -0
  24. package/dist/src/sessions/adapters/hermes.js +341 -0
  25. package/dist/src/sessions/adapters/openclaw.js +381 -0
  26. package/dist/src/sessions/adapters/opencode.js +454 -0
  27. package/dist/src/sessions/adapters/openhuman.js +315 -0
  28. package/dist/src/sessions/adapters/warp.js +160 -0
  29. package/dist/src/sessions/adapters/windsurf.js +212 -0
  30. package/dist/src/sessions/candidates.js +210 -0
  31. package/dist/src/sessions/contracts.js +310 -0
  32. package/dist/src/sessions/discovery.js +51 -0
  33. package/dist/src/sessions/fixtures.js +12 -0
  34. package/dist/src/sessions/importer.js +315 -0
  35. package/dist/src/sessions/index.js +25 -0
  36. package/dist/src/sessions/knowledge-shard.js +61 -0
  37. package/dist/src/sessions/optional-backends.js +238 -0
  38. package/dist/src/sessions/policy.js +192 -0
  39. package/dist/src/sessions/ports.js +2 -0
  40. package/dist/src/sessions/promotion.js +367 -0
  41. package/dist/src/sessions/readers.js +176 -0
  42. package/dist/src/sessions/repository.js +1551 -0
  43. package/dist/src/skills/adapters/agent-skills.js +59 -0
  44. package/dist/src/skills/adapters/local.js +19 -1
  45. package/dist/src/skills/agent-skills.js +249 -0
  46. package/dist/src/skills/cli.js +463 -7
  47. package/dist/src/skills/deployer.js +554 -0
  48. package/dist/src/skills/doctor.js +105 -0
  49. package/dist/src/skills/exporter.js +382 -0
  50. package/dist/src/skills/importer.js +921 -0
  51. package/dist/src/skills/registry.js +19 -0
  52. package/dist/src/skills/validator.js +323 -0
  53. package/package.json +2 -2
@@ -0,0 +1,341 @@
1
+ import { z } from 'zod';
2
+ import { SessionContractError, assertSupportedSchemaMajor, } from '../contracts.js';
3
+ import { readBoundedJsonLines, streamBoundedJsonLines, } from '../readers.js';
4
+ export const HERMES_ADAPTER_VERSION = '1.0.0';
5
+ export const HERMES_EXPORT_SCHEMA_VERSION = '1.0.0';
6
+ export const HERMES_NATIVE_SCHEMA_VERSION = '23.0.0';
7
+ const MessageSchema = z.object({
8
+ id: z.union([z.string(), z.number()]),
9
+ role: z.string().min(1),
10
+ content: z.unknown().optional(),
11
+ tool_call_id: z.string().optional(),
12
+ tool_calls: z.unknown().optional(),
13
+ tool_name: z.string().optional(),
14
+ timestamp: z.number().optional(),
15
+ token_count: z.number().optional(),
16
+ finish_reason: z.string().optional(),
17
+ reasoning: z.string().optional(),
18
+ reasoning_content: z.string().optional(),
19
+ reasoning_details: z.unknown().optional(),
20
+ codex_reasoning_items: z.unknown().optional(),
21
+ }).passthrough();
22
+ const ExportSchema = z.object({
23
+ schemaVersion: z.union([z.string(), z.number()]),
24
+ id: z.string().min(1),
25
+ source: z.string().min(1),
26
+ user_id: z.string().nullable().optional(),
27
+ model: z.string().nullable().optional(),
28
+ model_config: z.unknown().optional(),
29
+ parent_session_id: z.string().nullable().optional(),
30
+ compressed_from_session_id: z.string().nullable().optional(),
31
+ started_at: z.number(),
32
+ ended_at: z.number().nullable().optional(),
33
+ end_reason: z.string().nullable().optional(),
34
+ archived: z.boolean().optional(),
35
+ inactive: z.boolean().optional(),
36
+ cwd: z.string().nullable().optional(),
37
+ git_branch: z.string().nullable().optional(),
38
+ routing_key: z.string().nullable().optional(),
39
+ input_tokens: z.number().optional(),
40
+ output_tokens: z.number().optional(),
41
+ cache_read_tokens: z.number().optional(),
42
+ cache_write_tokens: z.number().optional(),
43
+ reasoning_tokens: z.number().optional(),
44
+ estimated_cost_usd: z.number().nullable().optional(),
45
+ actual_cost_usd: z.number().nullable().optional(),
46
+ billing_provider: z.string().nullable().optional(),
47
+ messages: z.array(MessageSchema),
48
+ export_deleted_at: z.number().nullable().optional(),
49
+ provider_deleted_at: z.number().nullable().optional(),
50
+ snapshotConsistency: z.enum(['sqlite-backup']).optional(),
51
+ }).passthrough();
52
+ export class HermesSessionAdapter {
53
+ limits;
54
+ localApi;
55
+ provider = 'hermes';
56
+ adapterVersion = HERMES_ADAPTER_VERSION;
57
+ disposition = 'implemented';
58
+ supportedOperations = ['inspect', 'stream'];
59
+ acquisitionModes = ['jsonl', 'api', 'sqlite-snapshot'];
60
+ constructor(limits, localApi) {
61
+ this.limits = limits;
62
+ this.localApi = localApi;
63
+ }
64
+ async *discover(_scope) {
65
+ // Native exports and consistent snapshots require explicit selection.
66
+ }
67
+ async inspect(source) {
68
+ const parsed = await this.readSource(source);
69
+ return {
70
+ sourceSchemaVersion: parsed.schemaVersion,
71
+ consistency: parsed.consistency,
72
+ operationalState: 'available',
73
+ };
74
+ }
75
+ async *stream(source, cursor) {
76
+ if (source.locatorClass === 'hermes-export-jsonl'
77
+ || source.locatorClass === 'hermes-consistent-snapshot-jsonl') {
78
+ const input = await streamBoundedJsonLines({
79
+ selectedPath: source.locator,
80
+ allowedRoots: source.authorizedScope.allowedRoots,
81
+ }, {
82
+ consistency: source.locatorClass === 'hermes-export-jsonl' ? 'provisional' : 'complete',
83
+ limits: this.limits,
84
+ });
85
+ const start = parseCursor(cursor?.value);
86
+ let outputIndex = 0;
87
+ let schema = null;
88
+ let sawRecord = false;
89
+ for await (const line of input) {
90
+ sawRecord = true;
91
+ const parsed = ExportSchema.safeParse(line.value);
92
+ if (!parsed.success) {
93
+ throw new SessionContractError('MALFORMED_SOURCE', 'Hermes export is malformed');
94
+ }
95
+ const currentSchema = declaredSchema(parsed.data);
96
+ if (schema && schema !== currentSchema) {
97
+ throw new SessionContractError('SCHEMA_DRIFT', 'mixed Hermes export schemas');
98
+ }
99
+ schema = currentSchema;
100
+ assertSupportedSchemaMajor(schema, 23);
101
+ if (source.locatorClass === 'hermes-consistent-snapshot-jsonl'
102
+ && parsed.data.snapshotConsistency !== 'sqlite-backup') {
103
+ throw new SessionContractError('SCHEMA_DRIFT', 'Hermes snapshot lacks sqlite3.backup() consistency evidence');
104
+ }
105
+ for (const record of normalizeSession(parsed.data, line, source.locatorClass)) {
106
+ if (outputIndex++ >= start)
107
+ yield record;
108
+ }
109
+ }
110
+ if (!sawRecord)
111
+ throw new SessionContractError('MALFORMED_SOURCE', 'Hermes source is empty');
112
+ if (input.incompleteTail && source.locatorClass !== 'hermes-export-jsonl') {
113
+ throw new SessionContractError('TRUNCATED_SOURCE', 'Hermes consistent snapshot is truncated');
114
+ }
115
+ return;
116
+ }
117
+ const parsed = await this.readSource(source);
118
+ const start = parseCursor(cursor?.value);
119
+ for (const record of parsed.records.slice(start))
120
+ yield record;
121
+ }
122
+ async readSource(source) {
123
+ if (source.locatorClass === 'hermes-state-db') {
124
+ throw new SessionContractError('UNSUPPORTED_OPERATION', 'raw Hermes state.db copying is unsafe in WAL mode; use native export or sqlite3.backup()');
125
+ }
126
+ let input;
127
+ if (source.locatorClass === 'hermes-local-api') {
128
+ if (source.authorizedScope.networkOperation !== 'hermes.local.sessions.read') {
129
+ throw new SessionContractError('OPERATION_NOT_AUTHORIZED', 'Hermes local API requires explicit hermes.local.sessions.read authorization');
130
+ }
131
+ if (!this.localApi) {
132
+ throw new SessionContractError('UNSUPPORTED_OPERATION', 'Hermes local API was not negotiated');
133
+ }
134
+ input = (await this.localApi.snapshot(source)).map((value, sequence) => ({
135
+ value, sequence, byteOffset: sequence, byteLength: Buffer.byteLength(JSON.stringify(value)),
136
+ }));
137
+ }
138
+ else if (source.locatorClass === 'hermes-export-jsonl'
139
+ || source.locatorClass === 'hermes-consistent-snapshot-jsonl') {
140
+ const result = await readBoundedJsonLines({
141
+ selectedPath: source.locator,
142
+ allowedRoots: source.authorizedScope.allowedRoots,
143
+ }, {
144
+ consistency: source.locatorClass === 'hermes-export-jsonl' ? 'provisional' : 'complete',
145
+ limits: this.limits,
146
+ });
147
+ input = result.records;
148
+ if (result.incompleteTail && source.locatorClass !== 'hermes-export-jsonl') {
149
+ throw new SessionContractError('TRUNCATED_SOURCE', 'Hermes consistent snapshot is truncated');
150
+ }
151
+ }
152
+ else {
153
+ throw new SessionContractError('UNSUPPORTED_OPERATION', 'unsupported Hermes source class');
154
+ }
155
+ if (input.length === 0)
156
+ throw new SessionContractError('MALFORMED_SOURCE', 'Hermes source is empty');
157
+ const parsed = input.map((line) => {
158
+ const result = ExportSchema.safeParse(line.value);
159
+ if (!result.success)
160
+ throw new SessionContractError('MALFORMED_SOURCE', 'Hermes export is malformed');
161
+ return { line, value: result.data };
162
+ });
163
+ const schemaVersion = declaredVersion(parsed.map(({ value }) => value));
164
+ assertSupportedSchemaMajor(schemaVersion, 23);
165
+ if (source.locatorClass === 'hermes-consistent-snapshot-jsonl'
166
+ && parsed.some(({ value }) => value.snapshotConsistency !== 'sqlite-backup')) {
167
+ throw new SessionContractError('SCHEMA_DRIFT', 'Hermes snapshot lacks sqlite3.backup() consistency evidence');
168
+ }
169
+ return {
170
+ schemaVersion: HERMES_EXPORT_SCHEMA_VERSION,
171
+ consistency: parsed.every(({ value }) => ended(value)) ? 'complete' : 'provisional',
172
+ records: parsed.flatMap(({ line, value }) => normalizeSession(value, line, source.locatorClass)),
173
+ };
174
+ }
175
+ }
176
+ function normalizeSession(session, line, locatorClass) {
177
+ const lifecycle = session.provider_deleted_at
178
+ ? 'deleted'
179
+ : session.archived ? 'archived' : ended(session) ? 'complete' : 'active';
180
+ const common = {
181
+ lifecycle,
182
+ lineage: {
183
+ parentSessionId: session.parent_session_id,
184
+ compressedFromSessionId: session.compressed_from_session_id,
185
+ },
186
+ workspace: {
187
+ cwdClass: session.cwd ? '<workspace>' : undefined,
188
+ gitBranch: session.git_branch,
189
+ routingKey: session.routing_key,
190
+ source: session.source,
191
+ userId: session.user_id,
192
+ },
193
+ model: session.model,
194
+ modelConfig: session.model_config,
195
+ usage: {
196
+ inputTokens: session.input_tokens,
197
+ outputTokens: session.output_tokens,
198
+ cacheReadTokens: session.cache_read_tokens,
199
+ cacheWriteTokens: session.cache_write_tokens,
200
+ reasoningTokens: session.reasoning_tokens,
201
+ estimatedCostUsd: session.estimated_cost_usd,
202
+ actualCostUsd: session.actual_cost_usd,
203
+ billingProvider: session.billing_provider,
204
+ },
205
+ deletion: {
206
+ exportDeletedAt: timestamp(session.export_deleted_at),
207
+ providerDeletedAt: timestamp(session.provider_deleted_at),
208
+ archivePreservesProviderData: true,
209
+ compactionPreservesLineage: true,
210
+ },
211
+ provenance: {
212
+ acquisition: locatorClass,
213
+ schema: declaredSchema(session),
214
+ sqliteSnapshotConsistency: session.snapshotConsistency,
215
+ },
216
+ sessionUnknownFields: unknownFields(session, SESSION_KEYS),
217
+ };
218
+ const header = {
219
+ nativeSessionId: session.id,
220
+ nativeEventId: `session:${session.id}`,
221
+ sequence: line.sequence * 1_000_000,
222
+ kind: 'hermes.session',
223
+ role: 'system',
224
+ occurredAt: timestamp(session.started_at),
225
+ text: '',
226
+ rawReference: { locatorClass, offset: line.byteOffset },
227
+ extensions: common,
228
+ };
229
+ return [
230
+ header,
231
+ ...session.messages.flatMap((message, index) => normalizeMessage(session.id, message, index, line, locatorClass, common)),
232
+ ];
233
+ }
234
+ function normalizeMessage(sessionId, message, index, line, locatorClass, common) {
235
+ const base = {
236
+ nativeSessionId: sessionId,
237
+ sequence: line.sequence * 1_000_000 + index * 10 + 1,
238
+ role: message.role,
239
+ occurredAt: timestamp(message.timestamp),
240
+ rawReference: { locatorClass, offset: line.byteOffset },
241
+ };
242
+ const records = [{
243
+ ...base,
244
+ nativeEventId: `message:${message.id}`,
245
+ kind: message.tool_name ? 'tool-result' : 'message',
246
+ participant: message.role,
247
+ toolName: message.tool_name,
248
+ toolCallId: message.tool_call_id,
249
+ text: extractText(message.content),
250
+ extensions: {
251
+ ...common,
252
+ toolCallId: message.tool_call_id,
253
+ toolName: message.tool_name,
254
+ tokenCount: message.token_count,
255
+ finishReason: message.finish_reason,
256
+ reasoning: {
257
+ text: message.reasoning,
258
+ content: message.reasoning_content,
259
+ details: message.reasoning_details,
260
+ codexItems: message.codex_reasoning_items,
261
+ },
262
+ opaqueContent: typeof message.content !== 'string',
263
+ unknownFields: unknownFields(message, MESSAGE_KEYS),
264
+ },
265
+ }];
266
+ const toolCalls = arrayValue(message.tool_calls);
267
+ toolCalls.forEach((call, callIndex) => records.push({
268
+ ...base,
269
+ nativeEventId: stringValue(call.id) || `message:${message.id}:tool:${callIndex}`,
270
+ sequence: base.sequence + callIndex + 1,
271
+ kind: 'tool-call',
272
+ participant: message.role,
273
+ toolName: stringValue(asObject(call.function).name),
274
+ toolCallId: stringValue(call.id),
275
+ text: stringValue(asObject(call.function).name),
276
+ extensions: { ...common, toolCall: call },
277
+ }));
278
+ return records;
279
+ }
280
+ function ended(value) {
281
+ return Boolean(value.ended_at || value.provider_deleted_at || value.archived || value.inactive);
282
+ }
283
+ function declaredVersion(values) {
284
+ const versions = new Set(values.map(declaredSchema));
285
+ if (versions.size !== 1)
286
+ throw new SessionContractError('SCHEMA_DRIFT', 'mixed Hermes export schemas');
287
+ return [...versions][0];
288
+ }
289
+ function declaredSchema(value) {
290
+ if (typeof value.schemaVersion === 'number')
291
+ return `${value.schemaVersion}.0.0`;
292
+ return /^\d+$/.test(value.schemaVersion) ? `${value.schemaVersion}.0.0` : value.schemaVersion;
293
+ }
294
+ function timestamp(value) {
295
+ if (value === undefined || value === null)
296
+ return undefined;
297
+ const date = new Date(value < 10_000_000_000 ? value * 1_000 : value);
298
+ return Number.isNaN(date.getTime()) ? undefined : date.toISOString();
299
+ }
300
+ function extractText(value) {
301
+ if (typeof value === 'string')
302
+ return value;
303
+ if (Array.isArray(value)) {
304
+ return value.map((item) => stringValue(asObject(item).text)).filter(Boolean).join('\n');
305
+ }
306
+ return '';
307
+ }
308
+ function arrayValue(value) {
309
+ return Array.isArray(value) ? value.map(asObject) : [];
310
+ }
311
+ function asObject(value) {
312
+ return value !== null && typeof value === 'object' && !Array.isArray(value)
313
+ ? value : {};
314
+ }
315
+ function stringValue(value) {
316
+ return typeof value === 'string' ? value : '';
317
+ }
318
+ function unknownFields(value, keys) {
319
+ return Object.fromEntries(Object.entries(value).filter(([key]) => !keys.has(key)).sort(([a], [b]) => a.localeCompare(b)));
320
+ }
321
+ function parseCursor(value) {
322
+ if (!value)
323
+ return 0;
324
+ if (!/^\d+$/.test(value))
325
+ throw new SessionContractError('SCHEMA_DRIFT', 'invalid Hermes cursor');
326
+ return Number(value);
327
+ }
328
+ const SESSION_KEYS = new Set([
329
+ 'schemaVersion', 'id', 'source', 'user_id', 'model', 'model_config',
330
+ 'parent_session_id', 'compressed_from_session_id', 'started_at', 'ended_at',
331
+ 'end_reason', 'archived', 'inactive', 'cwd', 'git_branch', 'routing_key',
332
+ 'input_tokens', 'output_tokens', 'cache_read_tokens', 'cache_write_tokens',
333
+ 'reasoning_tokens', 'estimated_cost_usd', 'actual_cost_usd', 'billing_provider',
334
+ 'messages', 'export_deleted_at', 'provider_deleted_at', 'snapshotConsistency',
335
+ ]);
336
+ const MESSAGE_KEYS = new Set([
337
+ 'id', 'role', 'content', 'tool_call_id', 'tool_calls', 'tool_name', 'timestamp',
338
+ 'token_count', 'finish_reason', 'reasoning', 'reasoning_content',
339
+ 'reasoning_details', 'codex_reasoning_items',
340
+ ]);
341
+ //# sourceMappingURL=hermes.js.map