@aiwg/cli 2026.7.20 → 2026.7.23

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 (62) hide show
  1. package/README.md +18 -7
  2. package/dist/src/api/index.d.ts +2 -0
  3. package/dist/src/api/index.js +2 -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 +1265 -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/config/aiwg-config.js +12 -0
  17. package/dist/src/config/cli.js +16 -3
  18. package/dist/src/extensions/commands/definitions.js +29 -0
  19. package/dist/src/extensions/manifest.js +29 -0
  20. package/dist/src/security/threat-assessment-config.js +296 -0
  21. package/dist/src/sessions/adapters/claude.js +385 -0
  22. package/dist/src/sessions/adapters/codex.js +548 -0
  23. package/dist/src/sessions/adapters/copilot.js +226 -0
  24. package/dist/src/sessions/adapters/cursor.js +528 -0
  25. package/dist/src/sessions/adapters/factory.js +386 -0
  26. package/dist/src/sessions/adapters/generic.js +225 -0
  27. package/dist/src/sessions/adapters/hermes.js +341 -0
  28. package/dist/src/sessions/adapters/openclaw.js +381 -0
  29. package/dist/src/sessions/adapters/opencode.js +454 -0
  30. package/dist/src/sessions/adapters/openhuman.js +315 -0
  31. package/dist/src/sessions/adapters/warp.js +160 -0
  32. package/dist/src/sessions/adapters/windsurf.js +212 -0
  33. package/dist/src/sessions/batch-contracts.js +121 -0
  34. package/dist/src/sessions/batch-import.js +265 -0
  35. package/dist/src/sessions/candidates.js +210 -0
  36. package/dist/src/sessions/contracts.js +337 -0
  37. package/dist/src/sessions/discovery.js +51 -0
  38. package/dist/src/sessions/fixtures.js +12 -0
  39. package/dist/src/sessions/import-lease.js +152 -0
  40. package/dist/src/sessions/importer.js +464 -0
  41. package/dist/src/sessions/index.js +31 -0
  42. package/dist/src/sessions/knowledge-shard.js +61 -0
  43. package/dist/src/sessions/optional-backends.js +238 -0
  44. package/dist/src/sessions/origin.js +117 -0
  45. package/dist/src/sessions/policy.js +192 -0
  46. package/dist/src/sessions/ports.js +2 -0
  47. package/dist/src/sessions/promotion.js +367 -0
  48. package/dist/src/sessions/readers.js +176 -0
  49. package/dist/src/sessions/repository.js +1892 -0
  50. package/dist/src/sessions/timeline.js +148 -0
  51. package/dist/src/sessions/workspace-discovery.js +319 -0
  52. package/dist/src/skills/adapters/agent-skills.js +59 -0
  53. package/dist/src/skills/adapters/local.js +19 -1
  54. package/dist/src/skills/agent-skills.js +249 -0
  55. package/dist/src/skills/cli.js +463 -7
  56. package/dist/src/skills/deployer.js +554 -0
  57. package/dist/src/skills/doctor.js +105 -0
  58. package/dist/src/skills/exporter.js +382 -0
  59. package/dist/src/skills/importer.js +921 -0
  60. package/dist/src/skills/registry.js +19 -0
  61. package/dist/src/skills/validator.js +323 -0
  62. package/package.json +2 -2
@@ -0,0 +1,148 @@
1
+ export const TIMELINE_SCHEMA_VERSION = '1.0.0';
2
+ export const DEFAULT_TIMELINE_GAP_MS = 30 * 60 * 1_000;
3
+ export function deriveSessionTimeline(input, gapMs = DEFAULT_TIMELINE_GAP_MS) {
4
+ if (!Number.isFinite(gapMs) || gapMs < 0) {
5
+ throw new Error('timeline gap must be a non-negative duration');
6
+ }
7
+ const grouped = new Map();
8
+ for (const item of input) {
9
+ const group = grouped.get(item.session.sessionId) ?? [];
10
+ group.push(item);
11
+ grouped.set(item.session.sessionId, group);
12
+ }
13
+ const result = [];
14
+ for (const items of grouped.values()) {
15
+ const timed = items
16
+ .filter((item) => item.event.occurredAt !== null)
17
+ .sort(compareTimelineInput);
18
+ const untimed = items
19
+ .filter((item) => item.event.occurredAt === null)
20
+ .sort(compareSequence);
21
+ if (timed.length === 0) {
22
+ const first = items[0];
23
+ result.push({
24
+ schemaVersion: TIMELINE_SCHEMA_VERSION,
25
+ provider: first.session.provider,
26
+ sessionId: first.session.sessionId,
27
+ segmentIndex: 0,
28
+ startAt: null,
29
+ endAt: null,
30
+ durationMs: null,
31
+ eventCount: untimed.length,
32
+ boundaryBasis: 'unknown-time',
33
+ boundaryEvidence: null,
34
+ confidence: 'low',
35
+ minSequence: untimed.at(0)?.event.sequence ?? 0,
36
+ maxSequence: untimed.at(-1)?.event.sequence ?? 0,
37
+ });
38
+ continue;
39
+ }
40
+ const sessionSegments = [];
41
+ let current = null;
42
+ let previous = null;
43
+ for (const item of timed) {
44
+ const boundary = previous ? segmentBoundary(previous.event, item.event, gapMs) : null;
45
+ if (!current || boundary) {
46
+ current = {
47
+ schemaVersion: TIMELINE_SCHEMA_VERSION,
48
+ provider: item.session.provider,
49
+ sessionId: item.session.sessionId,
50
+ segmentIndex: sessionSegments.length,
51
+ startAt: item.event.occurredAt,
52
+ endAt: item.event.occurredAt,
53
+ durationMs: 0,
54
+ eventCount: 0,
55
+ boundaryBasis: boundary?.basis ?? 'session-start',
56
+ boundaryEvidence: boundary?.evidence ?? null,
57
+ confidence: boundary?.confidence ?? 'high',
58
+ minSequence: item.event.sequence,
59
+ maxSequence: item.event.sequence,
60
+ };
61
+ sessionSegments.push(current);
62
+ }
63
+ current.eventCount += 1;
64
+ current.endAt = item.event.occurredAt;
65
+ current.durationMs = Math.max(0, Date.parse(current.endAt) - Date.parse(current.startAt));
66
+ current.minSequence = Math.min(current.minSequence, item.event.sequence);
67
+ current.maxSequence = Math.max(current.maxSequence, item.event.sequence);
68
+ previous = item;
69
+ }
70
+ for (const item of untimed) {
71
+ const destination = sessionSegments.find((segment) => item.event.sequence <= segment.maxSequence) ?? sessionSegments.at(-1);
72
+ destination.eventCount += 1;
73
+ destination.minSequence = Math.min(destination.minSequence, item.event.sequence);
74
+ destination.maxSequence = Math.max(destination.maxSequence, item.event.sequence);
75
+ }
76
+ result.push(...sessionSegments);
77
+ }
78
+ return result
79
+ .sort((left, right) => compareNullableTime(left.startAt, right.startAt)
80
+ || left.provider.localeCompare(right.provider)
81
+ || left.sessionId.localeCompare(right.sessionId)
82
+ || left.segmentIndex - right.segmentIndex)
83
+ .map(({ minSequence: _min, maxSequence: _max, ...segment }) => segment);
84
+ }
85
+ export function parseTimelineGap(value) {
86
+ if (value === undefined)
87
+ return DEFAULT_TIMELINE_GAP_MS;
88
+ const match = /^(\d+(?:\.\d+)?)(ms|s|m|h|d)$/.exec(value.trim());
89
+ if (!match)
90
+ throw new Error('timeline gap must use ms, s, m, h, or d (for example 30m)');
91
+ const factors = {
92
+ ms: 1,
93
+ s: 1_000,
94
+ m: 60_000,
95
+ h: 3_600_000,
96
+ d: 86_400_000,
97
+ };
98
+ const result = Number(match[1]) * factors[match[2]];
99
+ if (!Number.isSafeInteger(result) || result < 0) {
100
+ throw new Error('timeline gap is outside the supported duration range');
101
+ }
102
+ return result;
103
+ }
104
+ function segmentBoundary(previous, current, gapMs) {
105
+ if (current.activityBoundary === 'resume'
106
+ || current.activityBoundary === 'continuation') {
107
+ return {
108
+ basis: 'provider-explicit',
109
+ evidence: current.activityBoundaryBasis ?? current.activityBoundary,
110
+ confidence: current.activityBoundaryConfidence ?? 'high',
111
+ };
112
+ }
113
+ if (previous.activityBoundary === 'pause' || previous.activityBoundary === 'end') {
114
+ return {
115
+ basis: 'provider-explicit',
116
+ evidence: previous.activityBoundaryBasis ?? previous.activityBoundary,
117
+ confidence: previous.activityBoundaryConfidence ?? 'high',
118
+ };
119
+ }
120
+ const gap = Date.parse(current.occurredAt) - Date.parse(previous.occurredAt);
121
+ if (gap > gapMs) {
122
+ return {
123
+ basis: 'inferred-gap',
124
+ evidence: `inactivity>${gapMs}ms`,
125
+ confidence: 'medium',
126
+ };
127
+ }
128
+ return null;
129
+ }
130
+ function compareTimelineInput(left, right) {
131
+ return Date.parse(left.event.occurredAt) - Date.parse(right.event.occurredAt)
132
+ || left.event.sequence - right.event.sequence
133
+ || left.event.eventId.localeCompare(right.event.eventId);
134
+ }
135
+ function compareSequence(left, right) {
136
+ return left.event.sequence - right.event.sequence
137
+ || left.event.eventId.localeCompare(right.event.eventId);
138
+ }
139
+ function compareNullableTime(left, right) {
140
+ if (left === right)
141
+ return 0;
142
+ if (left === null)
143
+ return 1;
144
+ if (right === null)
145
+ return -1;
146
+ return Date.parse(left) - Date.parse(right);
147
+ }
148
+ //# sourceMappingURL=timeline.js.map
@@ -0,0 +1,319 @@
1
+ import { createReadStream } from 'node:fs';
2
+ import { access, mkdir, readFile, realpath, rename, stat, writeFile, } from 'node:fs/promises';
3
+ import { homedir, userInfo } from 'node:os';
4
+ import { basename, dirname, join, resolve } from 'node:path';
5
+ import { createInterface } from 'node:readline';
6
+ import { ClaudeSessionAdapter } from './adapters/claude.js';
7
+ import { CodexSessionAdapter } from './adapters/codex.js';
8
+ import { CursorSessionAdapter } from './adapters/cursor.js';
9
+ import { FactorySessionAdapter } from './adapters/factory.js';
10
+ import { SESSION_PROVIDER_IDS, sha256, } from './contracts.js';
11
+ import { redactSourceLocator } from './discovery.js';
12
+ import { fingerprintSourceFile } from './readers.js';
13
+ export const DISCOVERY_MANIFEST_VERSION = '1.0.0';
14
+ const MANUAL_EXPORT_PROVIDERS = new Set([
15
+ 'copilot', 'hermes', 'opencode', 'openclaw', 'openhuman',
16
+ 'warp', 'devin-desktop', 'generic',
17
+ ]);
18
+ export async function discoverWorkspaceHistories(options) {
19
+ const workspacePath = await canonicalPath(options.workspace);
20
+ const workspaceId = workspacePath;
21
+ const providerHomes = providerHomeCandidates(options.providerHome, options.operatorHome);
22
+ const keyWithLeadingDash = workspaceKey(workspacePath, true);
23
+ const keyWithoutLeadingDash = workspaceKey(workspacePath, false);
24
+ const discoverable = [
25
+ {
26
+ provider: 'claude',
27
+ adapter: new ClaudeSessionAdapter(),
28
+ roots: providerHomes.map((providerHome) => join(providerHome, '.claude', 'projects', keyWithLeadingDash)),
29
+ },
30
+ {
31
+ provider: 'codex',
32
+ adapter: new CodexSessionAdapter(),
33
+ roots: options.codexRoot
34
+ ? [resolve(options.codexRoot)]
35
+ : options.providerHome
36
+ ? [join(resolve(options.providerHome), '.codex', 'sessions')]
37
+ : [],
38
+ },
39
+ {
40
+ provider: 'cursor',
41
+ adapter: new CursorSessionAdapter(),
42
+ roots: providerHomes.map((providerHome) => join(providerHome, '.cursor', 'projects', keyWithoutLeadingDash, 'agent-transcripts')),
43
+ },
44
+ {
45
+ provider: 'factory',
46
+ adapter: new FactorySessionAdapter(),
47
+ roots: providerHomes.flatMap((providerHome) => [
48
+ join(providerHome, '.factory', 'projects', keyWithLeadingDash),
49
+ join(providerHome, '.factory', 'sessions', keyWithLeadingDash),
50
+ ]),
51
+ },
52
+ ];
53
+ const reports = new Map();
54
+ const candidates = [];
55
+ const seen = new Set();
56
+ for (const entry of discoverable) {
57
+ const availableRoots = [];
58
+ for (const root of entry.roots) {
59
+ if (await pathExists(root))
60
+ availableRoots.push(await canonicalPath(root));
61
+ }
62
+ if (availableRoots.length === 0) {
63
+ const codexNeedsAuthorization = entry.provider === 'codex'
64
+ && entry.roots.length === 0;
65
+ reports.set(entry.provider, providerReport(entry.provider, codexNeedsAuthorization ? 'export-required' : 'unavailable', codexNeedsAuthorization ? 'manual-export' : 'discoverable', [], codexNeedsAuthorization
66
+ ? 'SHARED_ROOT_AUTHORIZATION_REQUIRED'
67
+ : 'PROVIDER_ROOT_UNAVAILABLE', codexNeedsAuthorization
68
+ ? 'Pass --codex-root with an explicitly authorized Codex sessions or App Server export root.'
69
+ : `No authorized ${entry.provider} workspace history root was found.`));
70
+ continue;
71
+ }
72
+ const scope = {
73
+ workspaceId,
74
+ allowedRoots: availableRoots,
75
+ };
76
+ const providerSources = [];
77
+ for await (const descriptor of entry.adapter.discover(scope)) {
78
+ const locator = await canonicalPath(descriptor.locator);
79
+ if (entry.provider === 'codex'
80
+ && !await codexSourceMatchesWorkspace(locator, workspacePath))
81
+ continue;
82
+ const details = await stat(locator);
83
+ const authorizedRoot = scope.allowedRoots.find((root) => locator === root || locator.startsWith(`${root}/`));
84
+ if (!authorizedRoot)
85
+ continue;
86
+ const fingerprint = await fingerprintSourceFile({
87
+ selectedPath: locator,
88
+ allowedRoots: [authorizedRoot],
89
+ });
90
+ const dedupeKey = `${entry.provider}\0${fingerprint.digest}\0${fingerprint.size}`;
91
+ if (seen.has(dedupeKey))
92
+ continue;
93
+ seen.add(dedupeKey);
94
+ const source = sourceFromDescriptor(descriptor, locator, authorizedRoot, details, fingerprint.digest);
95
+ providerSources.push(source);
96
+ candidates.push(source);
97
+ }
98
+ reports.set(entry.provider, providerReport(entry.provider, 'checked', 'discoverable', providerSources, providerSources.length === 0 ? 'NO_WORKSPACE_SOURCES' : null, providerSources.length === 0
99
+ ? `The ${entry.provider} root was checked, but no source matched the authorized workspace.`
100
+ : null));
101
+ }
102
+ for (const provider of SESSION_PROVIDER_IDS) {
103
+ if (reports.has(provider))
104
+ continue;
105
+ const manual = MANUAL_EXPORT_PROVIDERS.has(provider);
106
+ reports.set(provider, providerReport(provider, manual ? 'export-required' : 'not-checked', manual ? 'manual-export' : 'unsupported', [], manual ? 'EXPLICIT_EXPORT_REQUIRED' : 'PROVIDER_NOT_CHECKED', manual
107
+ ? `Select and authorize a supported ${provider} export before importing it.`
108
+ : `No discovery strategy is registered for ${provider}.`));
109
+ }
110
+ const sources = candidates.sort(compareSources);
111
+ const providers = [...reports.values()].sort((a, b) => a.provider.localeCompare(b.provider));
112
+ const identity = {
113
+ schemaVersion: DISCOVERY_MANIFEST_VERSION,
114
+ workspaceId,
115
+ sources: sources.map((source) => ({
116
+ sourceId: source.sourceId,
117
+ provider: source.provider,
118
+ locatorClass: source.locatorClass,
119
+ locator: source.locator,
120
+ modifiedAt: source.modifiedAt,
121
+ sizeBytes: source.sizeBytes,
122
+ digest: source.digest,
123
+ })),
124
+ providers: providers.map((provider) => ({
125
+ provider: provider.provider,
126
+ status: provider.status,
127
+ reasonCode: provider.reasonCode,
128
+ })),
129
+ };
130
+ return {
131
+ schemaVersion: DISCOVERY_MANIFEST_VERSION,
132
+ manifestId: sha256(JSON.stringify(identity)),
133
+ createdAt: options.createdAt ?? new Date().toISOString(),
134
+ workspaceId,
135
+ workspacePath,
136
+ providers,
137
+ sources,
138
+ };
139
+ }
140
+ export function defaultDiscoveryManifestPath(workspace) {
141
+ return resolve(workspace, '.aiwg', 'sessions', 'discovery-manifest.json');
142
+ }
143
+ export async function writeDiscoveryManifest(path, manifest) {
144
+ await mkdir(dirname(path), { recursive: true, mode: 0o700 });
145
+ const temporary = `${path}.tmp-${process.pid}`;
146
+ await writeFile(temporary, `${JSON.stringify(manifest, null, 2)}\n`, { mode: 0o600 });
147
+ await rename(temporary, path);
148
+ }
149
+ export async function readDiscoveryManifest(path) {
150
+ const value = JSON.parse(await readFile(path, 'utf8'));
151
+ if (value.schemaVersion !== DISCOVERY_MANIFEST_VERSION
152
+ || typeof value.manifestId !== 'string'
153
+ || typeof value.workspaceId !== 'string'
154
+ || !Array.isArray(value.providers)
155
+ || !Array.isArray(value.sources)) {
156
+ throw new Error('session discovery manifest is malformed or unsupported');
157
+ }
158
+ const expected = await rediscoverManifestIdentity(value);
159
+ if (expected !== value.manifestId) {
160
+ throw new Error('session discovery manifest identity does not match its contents');
161
+ }
162
+ return value;
163
+ }
164
+ export function publicDiscoveryManifest(manifest) {
165
+ return {
166
+ schemaVersion: manifest.schemaVersion,
167
+ manifestId: manifest.manifestId,
168
+ createdAt: manifest.createdAt,
169
+ workspaceId: manifest.workspaceId,
170
+ workspacePath: manifest.workspacePath,
171
+ providers: manifest.providers,
172
+ sources: manifest.sources.map(({ locator: _locator, authorizedRoot: _root, ...source }) => source),
173
+ totals: {
174
+ providers: manifest.providers.length,
175
+ sources: manifest.sources.length,
176
+ },
177
+ };
178
+ }
179
+ function sourceFromDescriptor(descriptor, locator, authorizedRoot, details, digest) {
180
+ return {
181
+ sourceId: sha256([
182
+ 'workspace-source-v1', descriptor.provider, descriptor.locatorClass, digest,
183
+ ].join('\0')),
184
+ provider: descriptor.provider,
185
+ locator,
186
+ redactedLocator: redactSourceLocator(locator),
187
+ locatorClass: descriptor.locatorClass,
188
+ authorizedRoot,
189
+ modifiedAt: details.mtime.toISOString(),
190
+ sizeBytes: Number(details.size),
191
+ digest,
192
+ };
193
+ }
194
+ function providerReport(provider, status, disposition, sources, reasonCode, remediation) {
195
+ const timestamps = sources.map((source) => source.modifiedAt).sort();
196
+ return {
197
+ provider,
198
+ status,
199
+ disposition,
200
+ sourceCount: sources.length,
201
+ dateRange: {
202
+ earliest: timestamps.at(0) ?? null,
203
+ latest: timestamps.at(-1) ?? null,
204
+ },
205
+ dateRangeBasis: 'source-mtime',
206
+ reasonCode,
207
+ remediation,
208
+ };
209
+ }
210
+ async function rediscoverManifestIdentity(manifest) {
211
+ const identity = {
212
+ schemaVersion: manifest.schemaVersion,
213
+ workspaceId: manifest.workspaceId,
214
+ sources: manifest.sources.map((source) => ({
215
+ sourceId: source.sourceId,
216
+ provider: source.provider,
217
+ locatorClass: source.locatorClass,
218
+ locator: source.locator,
219
+ modifiedAt: source.modifiedAt,
220
+ sizeBytes: source.sizeBytes,
221
+ digest: source.digest,
222
+ })),
223
+ providers: manifest.providers.map((provider) => ({
224
+ provider: provider.provider,
225
+ status: provider.status,
226
+ reasonCode: provider.reasonCode,
227
+ })),
228
+ };
229
+ return sha256(JSON.stringify(identity));
230
+ }
231
+ async function codexSourceMatchesWorkspace(locator, workspacePath) {
232
+ const input = createReadStream(locator, { encoding: 'utf8' });
233
+ const lines = createInterface({ input, crlfDelay: Infinity });
234
+ let count = 0;
235
+ try {
236
+ for await (const line of lines) {
237
+ if (++count > 50)
238
+ break;
239
+ let value;
240
+ try {
241
+ value = JSON.parse(line);
242
+ }
243
+ catch {
244
+ continue;
245
+ }
246
+ const cwd = codexWorkspaceField(value);
247
+ if (!cwd)
248
+ continue;
249
+ try {
250
+ return await canonicalPath(cwd) === workspacePath;
251
+ }
252
+ catch {
253
+ return resolve(cwd) === workspacePath;
254
+ }
255
+ }
256
+ return false;
257
+ }
258
+ finally {
259
+ lines.close();
260
+ input.destroy();
261
+ }
262
+ }
263
+ function codexWorkspaceField(value) {
264
+ const root = asObject(value);
265
+ const payload = asObject(root.payload);
266
+ const result = asObject(root.result);
267
+ const resultThread = asObject(result.thread);
268
+ const params = asObject(root.params);
269
+ const paramsThread = asObject(params.thread);
270
+ for (const candidate of [
271
+ payload.cwd, result.cwd, resultThread.cwd, params.cwd, paramsThread.cwd,
272
+ ]) {
273
+ if (typeof candidate === 'string' && candidate.length > 0)
274
+ return candidate;
275
+ }
276
+ return null;
277
+ }
278
+ function asObject(value) {
279
+ return value !== null && typeof value === 'object' && !Array.isArray(value)
280
+ ? value
281
+ : {};
282
+ }
283
+ async function canonicalPath(path) {
284
+ return realpath(resolve(path));
285
+ }
286
+ function workspaceKey(path, retainLeadingSeparator) {
287
+ const normalized = path.replace(/\\/g, '/');
288
+ const input = retainLeadingSeparator ? normalized : normalized.replace(/^\/+/, '');
289
+ return input.replace(/[/:]+/g, '-');
290
+ }
291
+ function providerHomeCandidates(explicit, operatorHome) {
292
+ if (explicit)
293
+ return [resolve(explicit)];
294
+ let accountHome = operatorHome;
295
+ if (!accountHome) {
296
+ try {
297
+ accountHome = userInfo().homedir;
298
+ }
299
+ catch {
300
+ accountHome = undefined;
301
+ }
302
+ }
303
+ return [...new Set([homedir(), accountHome].filter((candidate) => Boolean(candidate)).map((candidate) => resolve(candidate)))];
304
+ }
305
+ async function pathExists(path) {
306
+ try {
307
+ await access(path);
308
+ return true;
309
+ }
310
+ catch {
311
+ return false;
312
+ }
313
+ }
314
+ function compareSources(left, right) {
315
+ return left.provider.localeCompare(right.provider)
316
+ || left.locator.localeCompare(right.locator)
317
+ || basename(left.locator).localeCompare(basename(right.locator));
318
+ }
319
+ //# sourceMappingURL=workspace-discovery.js.map
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Managed Agent Skills adapter.
3
+ *
4
+ * Imported sources remain separate from provider deployments. This adapter
5
+ * supports inspection and import only; provider projection is implemented by
6
+ * the deployment layer.
7
+ *
8
+ * @implements #1877
9
+ */
10
+ import fs from 'node:fs';
11
+ import path from 'node:path';
12
+ import { getImportedAgentSkill, importAgentSkill, listImportedAgentSkills, } from '../importer.js';
13
+ export class AgentSkillsAdapter {
14
+ projectDir;
15
+ id = 'agentskills';
16
+ name = 'Agent Skills (Managed Imports)';
17
+ constructor(projectDir) {
18
+ this.projectDir = projectDir;
19
+ }
20
+ resolveProjectDir() {
21
+ return this.projectDir ?? process.cwd();
22
+ }
23
+ async isAvailable() {
24
+ return true;
25
+ }
26
+ async list() {
27
+ return listImportedAgentSkills(this.resolveProjectDir()).map((record) => ({
28
+ name: record.name,
29
+ description: record.description,
30
+ source: this.id,
31
+ installed: true,
32
+ }));
33
+ }
34
+ async search(query) {
35
+ const normalized = query.toLowerCase();
36
+ return (await this.list()).filter((skill) => (skill.name.toLowerCase().includes(normalized)
37
+ || skill.description.toLowerCase().includes(normalized)));
38
+ }
39
+ async info(name) {
40
+ const record = getImportedAgentSkill(this.resolveProjectDir(), name);
41
+ if (!record)
42
+ return undefined;
43
+ const skillPath = path.join(record.managedLocation, 'SKILL.md');
44
+ const managedDrift = record.diagnostics.some((item) => item.code === 'AS_IMPORT_MANAGED_DRIFT');
45
+ return {
46
+ name: record.name,
47
+ description: record.description,
48
+ source: this.id,
49
+ installed: true,
50
+ path: skillPath,
51
+ content: managedDrift ? undefined : fs.readFileSync(skillPath, 'utf8'),
52
+ imported: record,
53
+ };
54
+ }
55
+ async importSource(source, options) {
56
+ return importAgentSkill(source, options);
57
+ }
58
+ }
59
+ //# sourceMappingURL=agent-skills.js.map
@@ -11,7 +11,25 @@ import path from 'path';
11
11
  import { fileURLToPath } from 'url';
12
12
  import { getProviderDefinition, resolveProviderPathValue, } from '../../providers/provider-definitions.js';
13
13
  const _scriptDir = path.dirname(fileURLToPath(import.meta.url));
14
- const AIWG_ROOT = process.env.AIWG_ROOT || path.resolve(_scriptDir, '../../../');
14
+ function resolveAiwgRoot() {
15
+ if (process.env.AIWG_ROOT)
16
+ return process.env.AIWG_ROOT;
17
+ const candidates = [
18
+ path.resolve(_scriptDir, '../../../'),
19
+ path.resolve(_scriptDir, '../../../../'),
20
+ ];
21
+ const repoRoot = candidates.find((candidate) => (fs.existsSync(path.join(candidate, 'package.json'))
22
+ && fs.existsSync(path.join(candidate, 'agentic', 'code'))));
23
+ if (repoRoot)
24
+ return repoRoot;
25
+ for (const candidate of candidates) {
26
+ if (fs.existsSync(path.join(candidate, 'agentic', 'code'))) {
27
+ return candidate;
28
+ }
29
+ }
30
+ return candidates[0];
31
+ }
32
+ const AIWG_ROOT = resolveAiwgRoot();
15
33
  function resolveSkillFallbackPath(target, projectDir, name) {
16
34
  const definition = getProviderDefinition(target);
17
35
  if (!definition)