@inneranimalmedia/agentsam-sdk 2.6.3 → 2.6.4
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/AGENTSAM.md +6 -0
- package/README.md +16 -1
- package/bin/agentsam +16 -1
- package/docs/BRAND_INTELLIGENCE.md +1 -1
- package/docs/architecture/AGENTSAM_DISTRIBUTION_OWNERSHIP.md +176 -0
- package/docs/architecture/AGENTSAM_GO_RUNTIME.md +282 -0
- package/docs/architecture/CODEBASEINDEX_GUIDED_PATH.md +202 -0
- package/docs/architecture/FS_E2E_CLOSURE_RECEIPT.md +59 -0
- package/docs/architecture/LOCAL_FS_AUTHORITY.md +38 -0
- package/docs/architecture/LOCAL_STUDIO_CLOUDFLARE_OAUTH.md +28 -0
- package/docs/architecture/PLAN_CLI_AND_LOCAL_STUDIO_DESKTOP.md +96 -0
- package/docs/architecture/SAM_ACTIVITY_RECOVERY_RECEIPT.md +39 -0
- package/docs/architecture/SAM_DECISION_WORK_RECEIPT.md +54 -0
- package/docs/architecture/SAM_KERNEL.md +181 -0
- package/docs/architecture/SAM_MACHINE_NORMALIZATION_PRECOMMIT_REPORT.md +208 -0
- package/docs/architecture/SLASH_SKILLS_PORTABLE.md +116 -0
- package/docs/architecture/fs-e2e-receipt.latest.json +42 -0
- package/docs/architecture/previews/codebaseindex-guided-path/CODEBASEINDEX_GUIDED_PATH.md +202 -0
- package/docs/architecture/previews/codebaseindex-guided-path/index.html +321 -0
- package/migrations/d1/0010_portable_tickets_memory.sql +140 -0
- package/migrations/d1/0011_agentsam_skill_v2.sql +185 -0
- package/migrations/d1/0011b_agentsam_skill_v2_cutover.sql +22 -0
- package/migrations/d1/0011c_agentsam_skill_v2_backfill.sql +76 -0
- package/migrations/d1/0011d_agentsam_skill_v2_retrieval_revisions.sql +50 -0
- package/migrations/d1/0012_agentsam_tools_required_seed.sql +67 -0
- package/migrations/d1/0013_identity_oauth_states.sql +15 -0
- package/migrations/d1/0014_auth_event_log.sql +20 -0
- package/migrations/d1/0015_identity_oauth_state_app_id.sql +3 -0
- package/migrations/d1/README_PORTABLE_CONTROL_PLANE.md +16 -0
- package/migrations/sqlite/agentsam_skill_retrieval.portable.sql +42 -0
- package/package.json +19 -4
- package/packages/agentsam-contracts/src/errors.ts +16 -0
- package/packages/agentsam-errors/src/envelope.js +53 -0
- package/packages/agentsam-errors/src/index.js +1 -0
- package/packages/agentsam-errors/src/recovery.js +301 -0
- package/packages/agentsam-knowledge/src/providers/index.js +18 -6
- package/packages/connectors/cloudflare/src/routes.js +9 -0
- package/packages/connectors/cloudflare/tests/connector.test.mjs +26 -1
- package/packages/identity/.agentsam/features/oauth-login-portal/agentsam.feature.json +1 -1
- package/packages/identity/.agentsam/features/oauth-login-portal/routes.json +11 -2
- package/packages/identity/docs/PORTABLE_IDENTITY_ARCHITECTURE.md +50 -0
- package/packages/identity/migrations/D1_SCHEMA_MAPPING.md +31 -0
- package/packages/identity/migrations/sqlite/001_identity_core.sql +99 -0
- package/packages/identity/migrations/sqlite/002_identity_oauth_client.sql +38 -0
- package/packages/identity/migrations/sqlite/003_identity_oauth_server.sql +64 -0
- package/packages/identity/package.json +2 -2
- package/packages/identity/src/adapters/cloudflare-d1/index.js +122 -22
- package/packages/identity/src/adapters/sqlite/index.js +319 -0
- package/packages/identity/src/app/verify-app.js +95 -0
- package/packages/identity/src/contracts/identity-store.js +115 -0
- package/packages/identity/src/contracts/route-ids.js +30 -0
- package/packages/identity/src/contracts/route-projection.js +218 -0
- package/packages/identity/src/contracts/routes.js +11 -0
- package/packages/identity/src/core/browser-paths.js +4 -5
- package/packages/identity/src/core/constants.js +13 -8
- package/packages/identity/src/core/session-policy.js +32 -0
- package/packages/identity/src/frontend/auth-portal/pages/login.html +10 -10
- package/packages/identity/src/frontend/auth-portal/pages/reset.html +3 -3
- package/packages/identity/src/frontend/auth-portal/pages/signup.html +3 -3
- package/packages/identity/src/frontend/auth-portal/preview/dashboard-stub.html +1 -1
- package/packages/identity/src/index.js +25 -0
- package/packages/identity/src/oauth/README.md +10 -4
- package/packages/identity/src/oauth/credentials.js +20 -13
- package/packages/identity/src/oauth/finalize-inbound.js +1 -1
- package/packages/identity/src/oauth/iam-platform.js +8 -7
- package/packages/identity/src/oauth/redirect-paths.js +27 -36
- package/packages/identity/src/server/identity-service.js +30 -13
- package/packages/identity/src/server/mount-policy.js +30 -0
- package/packages/identity/src/server/post-auth.js +79 -0
- package/packages/identity/src/server/worker-router.js +104 -72
- package/packages/identity/tests/finalize-inbound-oauth.test.mjs +6 -6
- package/packages/identity/tests/iam-provider.test.mjs +1 -1
- package/packages/identity/tests/identity-service.test.mjs +36 -5
- package/packages/identity/tests/oauth-credentials.test.mjs +3 -1
- package/packages/identity/tests/portable-identity-architecture.test.mjs +157 -0
- package/packages/identity/tests/session-routes-policy.test.mjs +21 -0
- package/packages/theme-church-site/package.json +2 -1
- package/packages/theme-church-site/src/index.js +1 -0
- package/packages/theme-companions-site/package.json +2 -1
- package/packages/theme-companions-site/src/index.js +1 -0
- package/packages/theme-floors-site/package.json +2 -1
- package/packages/theme-floors-site/src/index.js +1 -0
- package/packages/theme-fuelnfree-site/package.json +2 -1
- package/packages/theme-fuelnfree-site/src/index.js +1 -0
- package/packages/theme-handyman-site/package.json +2 -1
- package/packages/theme-handyman-site/src/index.js +1 -0
- package/packages/theme-insurance-site/package.json +2 -1
- package/packages/theme-insurance-site/src/index.js +1 -0
- package/packages/theme-shinshu-site/package.json +2 -1
- package/packages/theme-shinshu-site/src/index.js +1 -0
- package/protocol/apps/agentsam.app.v1.schema.json +51 -0
- package/protocol/brand/brandpack.v1.schema.json +43 -0
- package/protocol/credentials/issue.v1.schema.json +38 -0
- package/protocol/database/connection.v1.schema.json +41 -0
- package/protocol/embeddings/embedding-profile.v1.schema.json +20 -0
- package/protocol/errors/error-envelope.schema.json +135 -1
- package/protocol/errors/recovery.v1.schema.json +50 -0
- package/protocol/runtime/workspace-fs.v1.schema.json +71 -0
- package/protocol/sam/activity.v1.schema.json +48 -0
- package/protocol/sam/answer.v1.schema.json +35 -0
- package/protocol/sam/calibration.v1.schema.json +21 -0
- package/protocol/sam/decision-receipt.v1.schema.json +29 -0
- package/protocol/sam/evaluation.v1.schema.json +19 -0
- package/protocol/sam/operation.schema.json +66 -0
- package/protocol/sam/outcome.v1.schema.json +36 -0
- package/protocol/sam/question.v1.schema.json +26 -0
- package/protocol/sam/registry.seed.json +153 -0
- package/protocol/sam/result.schema.json +53 -0
- package/protocol/sam/state.v1.schema.json +23 -0
- package/protocol/skills/agentsam.interaction.v1.schema.json +50 -0
- package/protocol/skills/agentsam.skill.v1.schema.json +57 -0
- package/protocol/ui/icon-registry.mjs +226 -0
- package/protocol/ui/icon.v1.schema.json +51 -0
- package/skills/README.md +22 -9
- package/skills/agentsam-codebaseindex/SKILL.md +225 -0
- package/skills/catalog.json +14 -0
- package/src/cli/command-catalog.js +130 -0
- package/src/cli/dispatch.js +48 -0
- package/src/cli.js +43 -1
- package/src/commands/api-key.js +244 -0
- package/src/commands/app.js +60 -28
- package/src/commands/brand.js +17 -19
- package/src/commands/codebaseindex.js +688 -0
- package/src/commands/env.js +152 -25
- package/src/commands/go.js +366 -53
- package/src/commands/interaction-clack.js +115 -0
- package/src/commands/models.js +1 -1
- package/src/commands/providers.js +62 -14
- package/src/commands/shell.js +43 -2
- package/src/commands/skill.js +248 -0
- package/src/commands/skills.js +1 -1
- package/src/commands/start-local.js +4 -0
- package/src/commands/whoami.js +90 -18
- package/src/go/build.js +229 -39
- package/src/go/cloudflare.js +506 -105
- package/src/go/container.js +120 -0
- package/src/go/contract.js +9 -4
- package/src/go/discover.js +149 -33
- package/src/go/index.js +15 -3
- package/src/go/native-probe-runner.mjs +119 -0
- package/src/go/{registry.js → official-registry.js} +64 -7
- package/src/go/official-release.js +10 -0
- package/src/go/receipts.js +77 -10
- package/src/go/verify.js +9 -2
- package/src/index.js +27 -0
- package/src/indexing/ingest/discover-models.js +298 -0
- package/src/indexing/ingest/inventory.js +243 -0
- package/src/indexing/ingest/job-graph.js +181 -0
- package/src/indexing/ingest/materials.js +210 -0
- package/src/lib/provider-credentials.js +63 -21
- package/src/lib/slash-commands.js +1 -0
- package/src/local-fs/capability.js +121 -0
- package/src/local-fs/freshness.js +75 -0
- package/src/local-fs/index.js +385 -0
- package/src/local-fs/paths.js +100 -0
- package/src/local-pty/server.js +295 -19
- package/src/mcp/client.js +2 -2
- package/src/models/ai-access-onboarding.js +112 -0
- package/src/models/discovery.js +10 -2
- package/src/models/inventory-core.js +9 -1
- package/src/sam/activity/index.js +183 -0
- package/src/sam/client.js +252 -0
- package/src/sam/decision/calibration.js +109 -0
- package/src/sam/decision/confidence.js +126 -0
- package/src/sam/decision/evaluate.js +157 -0
- package/src/sam/decision/evaluators/deterministic.js +341 -0
- package/src/sam/decision/evaluators/heuristic.js +61 -0
- package/src/sam/decision/evaluators/select.js +50 -0
- package/src/sam/decision/evaluators/semantic.js +149 -0
- package/src/sam/decision/hierarchical.js +61 -0
- package/src/sam/decision/index.js +53 -0
- package/src/sam/decision/policy.js +86 -0
- package/src/sam/decision/questions.js +120 -0
- package/src/sam/decision/receipt.js +148 -0
- package/src/sam/decision/state.js +117 -0
- package/src/sam/decision/types.js +22 -0
- package/src/sam/decision/validate.js +200 -0
- package/src/sam/define.js +51 -0
- package/src/sam/index.js +65 -0
- package/src/sam/operations/brand-scan.js +62 -0
- package/src/sam/operations/cad-blender-inspect.js +36 -0
- package/src/sam/operations/codebaseindex-ingest.js +49 -0
- package/src/sam/operations/decision-evaluate.js +59 -0
- package/src/sam/operations/planning-astar.js +77 -0
- package/src/sam/operations/planning-goap.js +60 -0
- package/src/sam/operations/repository-inspect.js +72 -0
- package/src/sam/operations/security-scan.js +33 -0
- package/src/sam/operations/terminal-exec.js +29 -0
- package/src/sam/planning/astar.js +311 -0
- package/src/sam/planning/goap.js +177 -0
- package/src/sam/planning/index.js +21 -0
- package/src/sam/planning/state.js +84 -0
- package/src/sam/registry.js +48 -0
- package/src/sam/result.js +77 -0
- package/src/sam/seed.js +44 -0
- package/src/sam/types.js +91 -0
- package/src/skills/catalog.js +64 -0
- package/src/skills/content-resolver.js +124 -0
- package/src/skills/hosted-store.js +37 -0
- package/src/skills/index.js +29 -64
- package/src/skills/interaction.js +102 -0
- package/src/skills/local-store.js +228 -0
- package/src/skills/manifest.js +104 -0
- package/src/skills/metrics.js +31 -0
- package/src/skills/registry.js +184 -0
- package/src/skills/runtime.js +287 -0
- package/src/skills/slash.js +44 -0
- package/src/ui/cli/help.js +94 -101
- package/test/cli/api-key-env-whoami.test.mjs +129 -0
- package/test/cli/codebaseindex-plan-ux.test.mjs +37 -0
- package/test/cli/go.test.mjs +62 -5
- package/test/cli/skill-npm-and-env.test.mjs +52 -0
- package/test/cli/wireframes-go-registry.test.mjs +87 -2
- package/test/go/build-source-identity.test.mjs +31 -0
- package/test/go/cloudflare-probe.test.mjs +274 -10
- package/test/go/distribution.test.mjs +28 -0
- package/test/integration/ai-access-onboarding.test.mjs +47 -0
- package/test/integration/cli-help.test.mjs +1 -1
- package/test/integration/cms-site-tenancy-contract.test.mjs +6 -6
- package/test/integration/codebaseindex-ingest.test.mjs +166 -0
- package/test/integration/icon-registry.test.mjs +67 -0
- package/test/integration/ingest-discover-models.test.mjs +30 -0
- package/test/integration/install-script.test.mjs +12 -9
- package/test/integration/local-fs.test.mjs +113 -0
- package/test/integration/provider-env-cli.test.mjs +2 -1
- package/test/integration/sam-activity-recovery.test.mjs +147 -0
- package/test/integration/sam-decision.test.mjs +584 -0
- package/test/integration/sam-kernel.test.mjs +99 -0
- package/test/integration/sam-planning-astar.test.mjs +279 -0
- package/test/integration/skill-runtime.test.mjs +240 -0
- package/test/integration/studio-fs-pty-e2e.test.mjs +294 -0
- package/test/models.test.mjs +14 -7
- package/test/shell.test.mjs +4 -4
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { pathToFileURL } from 'node:url';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* SkillContentResolver — adapters only; runtime never imports R2/D1 directly.
|
|
7
|
+
*
|
|
8
|
+
* @typedef {{
|
|
9
|
+
* get?: (key: string) => Promise<string|null>|string|null,
|
|
10
|
+
* }} ObjectStoreAdapter
|
|
11
|
+
*
|
|
12
|
+
* @typedef {{
|
|
13
|
+
* getText?: (ref: string) => Promise<string|null>|string|null,
|
|
14
|
+
* }} DatabaseContentAdapter
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
export class SkillContentResolver {
|
|
18
|
+
/**
|
|
19
|
+
* @param {{
|
|
20
|
+
* objectStore?: ObjectStoreAdapter,
|
|
21
|
+
* database?: DatabaseContentAdapter,
|
|
22
|
+
* packageRootResolver?: (pkg: string) => string|null,
|
|
23
|
+
* }} [adapters]
|
|
24
|
+
*/
|
|
25
|
+
constructor(adapters = {}) {
|
|
26
|
+
this.objectStore = adapters.objectStore || null;
|
|
27
|
+
this.database = adapters.database || null;
|
|
28
|
+
this.packageRootResolver = adapters.packageRootResolver || null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @param {import('./manifest.js').SkillManifest} manifest
|
|
33
|
+
* @param {{ baseDir?: string, packageRoot?: string }} [ctx]
|
|
34
|
+
* @returns {Promise<{ content: string, source: string, ref: string|null }>}
|
|
35
|
+
*/
|
|
36
|
+
async resolve(manifest, ctx = {}) {
|
|
37
|
+
const source = manifest?.instructions?.source;
|
|
38
|
+
if (!source) throw new Error('instructions_source_required');
|
|
39
|
+
|
|
40
|
+
if (source === 'inline') {
|
|
41
|
+
const content = String(manifest.instructions.inline ?? '');
|
|
42
|
+
return { content, source, ref: null };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (source === 'local_file') {
|
|
46
|
+
const ref = String(manifest.instructions.ref || '').trim();
|
|
47
|
+
if (!ref) throw new Error('local_file_ref_required');
|
|
48
|
+
const base = ctx.baseDir || process.cwd();
|
|
49
|
+
const filePath = path.resolve(base, ref);
|
|
50
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
51
|
+
return { content, source, ref: filePath };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (source === 'package_file') {
|
|
55
|
+
const ref = String(manifest.instructions.ref || '').trim();
|
|
56
|
+
if (!ref) throw new Error('package_file_ref_required');
|
|
57
|
+
const root =
|
|
58
|
+
ctx.packageRoot ||
|
|
59
|
+
(manifest.package && this.packageRootResolver
|
|
60
|
+
? this.packageRootResolver(manifest.package)
|
|
61
|
+
: null) ||
|
|
62
|
+
ctx.baseDir;
|
|
63
|
+
if (!root) throw new Error('package_root_required');
|
|
64
|
+
const filePath = path.resolve(root, ref);
|
|
65
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
66
|
+
return { content, source, ref: filePath };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (source === 'database') {
|
|
70
|
+
const ref = String(manifest.instructions.ref || '').trim();
|
|
71
|
+
if (!ref) throw new Error('database_ref_required');
|
|
72
|
+
if (!this.database?.getText) {
|
|
73
|
+
throw new Error('database_content_adapter_required');
|
|
74
|
+
}
|
|
75
|
+
const content = await this.database.getText(ref);
|
|
76
|
+
if (content == null) throw new Error(`database_content_missing:${ref}`);
|
|
77
|
+
return { content: String(content), source, ref };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (source === 'object_store') {
|
|
81
|
+
const ref = String(manifest.instructions.ref || '').trim();
|
|
82
|
+
if (!ref) throw new Error('object_store_ref_required');
|
|
83
|
+
if (!this.objectStore?.get) {
|
|
84
|
+
throw new Error('object_store_adapter_required');
|
|
85
|
+
}
|
|
86
|
+
const content = await this.objectStore.get(ref);
|
|
87
|
+
if (content == null) throw new Error(`object_store_content_missing:${ref}`);
|
|
88
|
+
return { content: String(content), source, ref };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
throw new Error(`unsupported_instruction_source:${source}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Minimal local object-store adapter for tests (filesystem under a root).
|
|
97
|
+
* Cloudflare R2 would implement the same `.get(ref)` surface.
|
|
98
|
+
*/
|
|
99
|
+
export function createFilesystemObjectStore(rootDir) {
|
|
100
|
+
const root = path.resolve(rootDir);
|
|
101
|
+
return {
|
|
102
|
+
async get(key) {
|
|
103
|
+
const file = path.resolve(root, String(key).replace(/^\/+/, ''));
|
|
104
|
+
if (!file.startsWith(root + path.sep) && file !== root) {
|
|
105
|
+
throw new Error('object_store_path_escape');
|
|
106
|
+
}
|
|
107
|
+
if (!fs.existsSync(file)) return null;
|
|
108
|
+
return fs.readFileSync(file, 'utf8');
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function createMemoryDatabaseContent(map = new Map()) {
|
|
114
|
+
return {
|
|
115
|
+
async getText(ref) {
|
|
116
|
+
return map.has(ref) ? map.get(ref) : null;
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** @deprecated identity only — prefer path.resolve for package roots */
|
|
122
|
+
export function fileUrlDirname(importMetaUrl) {
|
|
123
|
+
return path.dirname(fileURLToPath(importMetaUrl));
|
|
124
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hosted SkillStore adapter contract — ownership lives here, not in agentsam.skill.v1.
|
|
3
|
+
* Portable runtime works with null store (local registry only).
|
|
4
|
+
*
|
|
5
|
+
* IAM migration: implement this against real account_id rows only.
|
|
6
|
+
* Never use account_id IN ('platform','system').
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @typedef {{
|
|
11
|
+
* listForAccount: (accountId: string) => Promise<Array<{ id: string, trigger: string, manifest: object }>>,
|
|
12
|
+
* getByTrigger: (accountId: string, trigger: string) => Promise<object|null>,
|
|
13
|
+
* put: (accountId: string, manifest: object, trigger: string) => Promise<void>,
|
|
14
|
+
* remove: (accountId: string, skillId: string) => Promise<void>,
|
|
15
|
+
* }} HostedSkillStore
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* No-op store for local / offline. Hosts inject a real D1-backed adapter later.
|
|
20
|
+
* @returns {HostedSkillStore}
|
|
21
|
+
*/
|
|
22
|
+
export function createNullHostedSkillStore() {
|
|
23
|
+
return {
|
|
24
|
+
async listForAccount() {
|
|
25
|
+
return [];
|
|
26
|
+
},
|
|
27
|
+
async getByTrigger() {
|
|
28
|
+
return null;
|
|
29
|
+
},
|
|
30
|
+
async put() {
|
|
31
|
+
throw new Error('hosted_skill_store_unavailable');
|
|
32
|
+
},
|
|
33
|
+
async remove() {
|
|
34
|
+
throw new Error('hosted_skill_store_unavailable');
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
}
|
package/src/skills/index.js
CHANGED
|
@@ -1,64 +1,29 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
if (resolved !== skillsRoot && !resolved.startsWith(rootWithSep)) {
|
|
31
|
-
throw new Error('skill_path_outside_root');
|
|
32
|
-
}
|
|
33
|
-
return resolved;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export function listSkills() {
|
|
37
|
-
return readCatalog().skills.map((skill) => ({ ...skill }));
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export function getSkill(idOrAlias) {
|
|
41
|
-
const skill = resolveSkillRecord(idOrAlias);
|
|
42
|
-
return skill ? { ...skill } : null;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export function loadSkill(idOrAlias, options = {}) {
|
|
46
|
-
const skill = resolveSkillRecord(idOrAlias);
|
|
47
|
-
if (!skill) throw new Error(`unknown_skill:${idOrAlias}`);
|
|
48
|
-
|
|
49
|
-
const result = {
|
|
50
|
-
skill: { ...skill },
|
|
51
|
-
instructions: fs.readFileSync(safeSkillPath(skill.entry), 'utf8'),
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
if (options.references === true) {
|
|
55
|
-
result.references = (skill.references ?? []).map((relativePath) => ({
|
|
56
|
-
path: relativePath,
|
|
57
|
-
content: fs.readFileSync(safeSkillPath(relativePath), 'utf8'),
|
|
58
|
-
}));
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return result;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export const AGENTSAM_SKILLS_ROOT = skillsRoot;
|
|
1
|
+
export { parseSlashInvocation, canonicalizeSlashTrigger, isExplicitSlashMessage } from './slash.js';
|
|
2
|
+
export {
|
|
3
|
+
interactionReady,
|
|
4
|
+
interactionComplete,
|
|
5
|
+
interactionBlocked,
|
|
6
|
+
interactionNeedsSelect,
|
|
7
|
+
interactionUnknownSkill,
|
|
8
|
+
} from './interaction.js';
|
|
9
|
+
export { normalizeSkillManifest, createBlankSkillManifest } from './manifest.js';
|
|
10
|
+
export { computeSkillContentMetrics } from './metrics.js';
|
|
11
|
+
export {
|
|
12
|
+
SkillContentResolver,
|
|
13
|
+
createFilesystemObjectStore,
|
|
14
|
+
createMemoryDatabaseContent,
|
|
15
|
+
} from './content-resolver.js';
|
|
16
|
+
export { SkillRegistry } from './registry.js';
|
|
17
|
+
export { SkillRuntime } from './runtime.js';
|
|
18
|
+
export {
|
|
19
|
+
createUserSkill,
|
|
20
|
+
installSkillFromPath,
|
|
21
|
+
aliasSkill,
|
|
22
|
+
removeSkill,
|
|
23
|
+
readLocalRegistry,
|
|
24
|
+
skillsHome,
|
|
25
|
+
} from './local-store.js';
|
|
26
|
+
|
|
27
|
+
// Legacy catalog surface (agentsam skills list / load)
|
|
28
|
+
export { listSkills, getSkill, loadSkill, AGENTSAM_SKILLS_ROOT } from './catalog.js';
|
|
29
|
+
export { createNullHostedSkillStore } from './hosted-store.js';
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AgentSamInteraction — host-agnostic next-step contract.
|
|
3
|
+
* Separate from modelInstructions (what the model sees this turn).
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @typedef {{
|
|
8
|
+
* status: 'ready'|'needs_input'|'needs_approval'|'blocked'|'complete',
|
|
9
|
+
* prompt?: {
|
|
10
|
+
* kind: 'info'|'text'|'select'|'multiselect'|'confirm'|'error',
|
|
11
|
+
* code?: string,
|
|
12
|
+
* title?: string,
|
|
13
|
+
* message: string,
|
|
14
|
+
* options?: Array<{ value: string, label: string, description?: string }>,
|
|
15
|
+
* actions?: Array<{ id: string, label: string }>,
|
|
16
|
+
* }
|
|
17
|
+
* }} AgentSamInteraction
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/** @returns {AgentSamInteraction} */
|
|
21
|
+
export function interactionReady() {
|
|
22
|
+
return { status: 'ready' };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** @returns {AgentSamInteraction} */
|
|
26
|
+
export function interactionComplete(message) {
|
|
27
|
+
return {
|
|
28
|
+
status: 'complete',
|
|
29
|
+
prompt: message
|
|
30
|
+
? { kind: 'info', message: String(message) }
|
|
31
|
+
: undefined,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @param {{ code?: string, title?: string, message: string, actions?: Array<{id:string,label:string}> }} opts
|
|
37
|
+
* @returns {AgentSamInteraction}
|
|
38
|
+
*/
|
|
39
|
+
export function interactionBlocked(opts) {
|
|
40
|
+
return {
|
|
41
|
+
status: 'blocked',
|
|
42
|
+
prompt: {
|
|
43
|
+
kind: 'error',
|
|
44
|
+
code: opts.code,
|
|
45
|
+
title: opts.title,
|
|
46
|
+
message: opts.message,
|
|
47
|
+
actions: opts.actions,
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* @param {{ title?: string, message: string, options: Array<{value:string,label:string,description?:string}> }} opts
|
|
54
|
+
* @returns {AgentSamInteraction}
|
|
55
|
+
*/
|
|
56
|
+
export function interactionNeedsSelect(opts) {
|
|
57
|
+
return {
|
|
58
|
+
status: 'needs_input',
|
|
59
|
+
prompt: {
|
|
60
|
+
kind: 'select',
|
|
61
|
+
title: opts.title,
|
|
62
|
+
message: opts.message,
|
|
63
|
+
options: opts.options,
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* @param {string} trigger
|
|
70
|
+
* @param {string[]} suggestions
|
|
71
|
+
* @returns {AgentSamInteraction}
|
|
72
|
+
*/
|
|
73
|
+
export function interactionUnknownSkill(trigger, suggestions = []) {
|
|
74
|
+
const lines = [
|
|
75
|
+
`Unknown skill ${trigger}`,
|
|
76
|
+
'',
|
|
77
|
+
...(suggestions.length
|
|
78
|
+
? ['Did you mean?', ...suggestions.map((s) => ` ${s}`), '']
|
|
79
|
+
: [
|
|
80
|
+
'No close matches in this registry.',
|
|
81
|
+
'',
|
|
82
|
+
'Create it locally:',
|
|
83
|
+
` agentsam skill create ${String(trigger).replace(/^\//, '') || 'my-skill'}`,
|
|
84
|
+
'',
|
|
85
|
+
]),
|
|
86
|
+
'List installed triggers with /skills, or create/install a skill when the list is empty.',
|
|
87
|
+
];
|
|
88
|
+
return {
|
|
89
|
+
status: 'blocked',
|
|
90
|
+
prompt: {
|
|
91
|
+
kind: 'error',
|
|
92
|
+
code: 'SKILL_NOT_FOUND',
|
|
93
|
+
title: 'Unknown skill',
|
|
94
|
+
message: lines.join('\n'),
|
|
95
|
+
actions: [
|
|
96
|
+
{ id: 'create', label: 'Create skill' },
|
|
97
|
+
{ id: 'list', label: 'List skills' },
|
|
98
|
+
{ id: 'cancel', label: 'Cancel' },
|
|
99
|
+
],
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { canonicalizeSlashTrigger } from './slash.js';
|
|
5
|
+
import { normalizeSkillManifest, createBlankSkillManifest } from './manifest.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Local install registry under ~/.agentsam/skills/
|
|
9
|
+
*
|
|
10
|
+
* {
|
|
11
|
+
* schema: 'agentsam.skill-registry.v1',
|
|
12
|
+
* entries: {
|
|
13
|
+
* [skillId]: {
|
|
14
|
+
* id, trigger, source: 'user'|'package'|'builtin'|'app',
|
|
15
|
+
* manifestPath, installedAt, package?, version?
|
|
16
|
+
* }
|
|
17
|
+
* },
|
|
18
|
+
* triggers: { '/foo': 'skill-id' }
|
|
19
|
+
* }
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
function homeDir(options = {}) {
|
|
23
|
+
return path.resolve(
|
|
24
|
+
String(options.home || options.env?.HOME || options.env?.USERPROFILE || os.homedir()),
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function skillsHome(options = {}) {
|
|
29
|
+
return path.join(homeDir(options), '.agentsam', 'skills');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function registryPath(options = {}) {
|
|
33
|
+
return path.join(skillsHome(options), 'registry.json');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function emptyRegistry() {
|
|
37
|
+
return {
|
|
38
|
+
schema: 'agentsam.skill-registry.v1',
|
|
39
|
+
entries: {},
|
|
40
|
+
triggers: {},
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function atomicWrite(file, data, mode = 0o600) {
|
|
45
|
+
const dir = path.dirname(file);
|
|
46
|
+
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
47
|
+
const tmp = `${file}.${process.pid}.${Date.now()}.tmp`;
|
|
48
|
+
fs.writeFileSync(tmp, data, { mode });
|
|
49
|
+
fs.renameSync(tmp, file);
|
|
50
|
+
if (process.platform !== 'win32') fs.chmodSync(file, mode);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function readLocalRegistry(options = {}) {
|
|
54
|
+
const file = registryPath(options);
|
|
55
|
+
if (!fs.existsSync(file)) return emptyRegistry();
|
|
56
|
+
try {
|
|
57
|
+
const parsed = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
58
|
+
return {
|
|
59
|
+
schema: 'agentsam.skill-registry.v1',
|
|
60
|
+
entries: parsed.entries && typeof parsed.entries === 'object' ? parsed.entries : {},
|
|
61
|
+
triggers: parsed.triggers && typeof parsed.triggers === 'object' ? parsed.triggers : {},
|
|
62
|
+
};
|
|
63
|
+
} catch {
|
|
64
|
+
return emptyRegistry();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function writeLocalRegistry(registry, options = {}) {
|
|
69
|
+
const file = registryPath(options);
|
|
70
|
+
atomicWrite(file, `${JSON.stringify(registry, null, 2)}\n`, 0o600);
|
|
71
|
+
return file;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* @param {string} skillId
|
|
76
|
+
* @param {{ trigger?: string, home?: string }} [options]
|
|
77
|
+
*/
|
|
78
|
+
export function createUserSkill(skillId, options = {}) {
|
|
79
|
+
const manifest = createBlankSkillManifest(skillId);
|
|
80
|
+
const trigger = options.trigger
|
|
81
|
+
? canonicalizeSlashTrigger(options.trigger)
|
|
82
|
+
: manifest.slash?.suggested || `/${manifest.id}`;
|
|
83
|
+
|
|
84
|
+
const root = path.join(skillsHome(options), 'user', manifest.id);
|
|
85
|
+
fs.mkdirSync(root, { recursive: true, mode: 0o700 });
|
|
86
|
+
const skillMd = path.join(root, 'SKILL.md');
|
|
87
|
+
fs.writeFileSync(skillMd, manifest.instructions.inline || '', { mode: 0o600 });
|
|
88
|
+
|
|
89
|
+
const onDisk = {
|
|
90
|
+
...manifest,
|
|
91
|
+
instructions: { source: 'local_file', ref: 'SKILL.md' },
|
|
92
|
+
};
|
|
93
|
+
const manifestFile = path.join(root, 'agentsam.skill.json');
|
|
94
|
+
atomicWrite(manifestFile, `${JSON.stringify(onDisk, null, 2)}\n`);
|
|
95
|
+
|
|
96
|
+
const registry = readLocalRegistry(options);
|
|
97
|
+
if (registry.entries[manifest.id]) throw new Error(`skill_exists:${manifest.id}`);
|
|
98
|
+
if (registry.triggers[trigger]) {
|
|
99
|
+
throw new Error(`slash_collision:${trigger}->${registry.triggers[trigger]}`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
registry.entries[manifest.id] = {
|
|
103
|
+
id: manifest.id,
|
|
104
|
+
trigger,
|
|
105
|
+
source: 'user',
|
|
106
|
+
manifestPath: manifestFile,
|
|
107
|
+
baseDir: root,
|
|
108
|
+
installedAt: new Date().toISOString(),
|
|
109
|
+
};
|
|
110
|
+
registry.triggers[trigger] = manifest.id;
|
|
111
|
+
writeLocalRegistry(registry, options);
|
|
112
|
+
return { manifest: onDisk, trigger, root, manifestPath: manifestFile };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Install from a directory containing agentsam.skill.json (or package root).
|
|
117
|
+
* @param {string} sourcePath
|
|
118
|
+
* @param {{ trigger?: string, aliasOnCollision?: string, home?: string }} [options]
|
|
119
|
+
*/
|
|
120
|
+
export function installSkillFromPath(sourcePath, options = {}) {
|
|
121
|
+
const abs = path.resolve(sourcePath);
|
|
122
|
+
const manifestFile = fs.existsSync(path.join(abs, 'agentsam.skill.json'))
|
|
123
|
+
? path.join(abs, 'agentsam.skill.json')
|
|
124
|
+
: abs.endsWith('agentsam.skill.json')
|
|
125
|
+
? abs
|
|
126
|
+
: null;
|
|
127
|
+
if (!manifestFile || !fs.existsSync(manifestFile)) {
|
|
128
|
+
throw new Error('agentsam.skill.json_required');
|
|
129
|
+
}
|
|
130
|
+
const baseDir = path.dirname(manifestFile);
|
|
131
|
+
const manifest = normalizeSkillManifest(JSON.parse(fs.readFileSync(manifestFile, 'utf8')));
|
|
132
|
+
const suggested = manifest.slash?.suggested || `/${manifest.id}`;
|
|
133
|
+
let trigger = options.trigger
|
|
134
|
+
? canonicalizeSlashTrigger(options.trigger)
|
|
135
|
+
: suggested;
|
|
136
|
+
|
|
137
|
+
const registry = readLocalRegistry(options);
|
|
138
|
+
const existingOwner = registry.triggers[trigger];
|
|
139
|
+
if (existingOwner && existingOwner !== manifest.id) {
|
|
140
|
+
if (!options.aliasOnCollision) {
|
|
141
|
+
const err = new Error(`slash_collision:${trigger}`);
|
|
142
|
+
err.code = 'SLASH_COLLISION';
|
|
143
|
+
err.suggested = suggested;
|
|
144
|
+
err.owner = existingOwner;
|
|
145
|
+
err.candidates = [`${trigger}-${manifest.id}`.slice(0, 64), `/${manifest.id}`];
|
|
146
|
+
throw err;
|
|
147
|
+
}
|
|
148
|
+
trigger = canonicalizeSlashTrigger(options.aliasOnCollision);
|
|
149
|
+
if (registry.triggers[trigger] && registry.triggers[trigger] !== manifest.id) {
|
|
150
|
+
throw new Error(`slash_collision:${trigger}`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Copy into install root for stability
|
|
155
|
+
const installRoot = path.join(skillsHome(options), 'installed', manifest.id);
|
|
156
|
+
fs.mkdirSync(installRoot, { recursive: true, mode: 0o700 });
|
|
157
|
+
const destManifest = path.join(installRoot, 'agentsam.skill.json');
|
|
158
|
+
atomicWrite(destManifest, `${JSON.stringify(manifest, null, 2)}\n`);
|
|
159
|
+
|
|
160
|
+
// Copy instruction file if package_file / local_file relative
|
|
161
|
+
if (
|
|
162
|
+
(manifest.instructions.source === 'local_file' ||
|
|
163
|
+
manifest.instructions.source === 'package_file') &&
|
|
164
|
+
manifest.instructions.ref
|
|
165
|
+
) {
|
|
166
|
+
const src = path.resolve(baseDir, manifest.instructions.ref);
|
|
167
|
+
if (fs.existsSync(src)) {
|
|
168
|
+
const dest = path.join(installRoot, path.basename(src));
|
|
169
|
+
fs.copyFileSync(src, dest);
|
|
170
|
+
manifest.instructions = {
|
|
171
|
+
source: 'local_file',
|
|
172
|
+
ref: path.basename(src),
|
|
173
|
+
};
|
|
174
|
+
atomicWrite(destManifest, `${JSON.stringify(manifest, null, 2)}\n`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Clear old trigger for this id if reinstalling
|
|
179
|
+
for (const [t, id] of Object.entries(registry.triggers)) {
|
|
180
|
+
if (id === manifest.id) delete registry.triggers[t];
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
registry.entries[manifest.id] = {
|
|
184
|
+
id: manifest.id,
|
|
185
|
+
trigger,
|
|
186
|
+
source: 'package',
|
|
187
|
+
manifestPath: destManifest,
|
|
188
|
+
baseDir: installRoot,
|
|
189
|
+
package: manifest.package || null,
|
|
190
|
+
version: manifest.version || null,
|
|
191
|
+
installedAt: new Date().toISOString(),
|
|
192
|
+
};
|
|
193
|
+
registry.triggers[trigger] = manifest.id;
|
|
194
|
+
writeLocalRegistry(registry, options);
|
|
195
|
+
return { id: manifest.id, trigger, manifestPath: destManifest };
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export function aliasSkill(skillId, triggerRaw, options = {}) {
|
|
199
|
+
const registry = readLocalRegistry(options);
|
|
200
|
+
const entry = registry.entries[skillId];
|
|
201
|
+
if (!entry) throw new Error(`unknown_skill:${skillId}`);
|
|
202
|
+
const trigger = canonicalizeSlashTrigger(triggerRaw);
|
|
203
|
+
const owner = registry.triggers[trigger];
|
|
204
|
+
if (owner && owner !== skillId) throw new Error(`slash_collision:${trigger}->${owner}`);
|
|
205
|
+
for (const [t, id] of Object.entries(registry.triggers)) {
|
|
206
|
+
if (id === skillId) delete registry.triggers[t];
|
|
207
|
+
}
|
|
208
|
+
registry.triggers[trigger] = skillId;
|
|
209
|
+
entry.trigger = trigger;
|
|
210
|
+
writeLocalRegistry(registry, options);
|
|
211
|
+
return { id: skillId, trigger };
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export function removeSkill(skillId, options = {}) {
|
|
215
|
+
const registry = readLocalRegistry(options);
|
|
216
|
+
if (!registry.entries[skillId]) return { removed: false };
|
|
217
|
+
delete registry.entries[skillId];
|
|
218
|
+
for (const [t, id] of Object.entries(registry.triggers)) {
|
|
219
|
+
if (id === skillId) delete registry.triggers[t];
|
|
220
|
+
}
|
|
221
|
+
writeLocalRegistry(registry, options);
|
|
222
|
+
return { removed: true, id: skillId };
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export function loadInstalledManifest(entry) {
|
|
226
|
+
const raw = JSON.parse(fs.readFileSync(entry.manifestPath, 'utf8'));
|
|
227
|
+
return normalizeSkillManifest(raw);
|
|
228
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { canonicalizeSlashTrigger } from './slash.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @typedef {{
|
|
5
|
+
* schema: 'agentsam.skill.v1',
|
|
6
|
+
* id: string,
|
|
7
|
+
* name: string,
|
|
8
|
+
* description?: string,
|
|
9
|
+
* icon?: string,
|
|
10
|
+
* version?: string,
|
|
11
|
+
* package?: string,
|
|
12
|
+
* slash?: { suggested?: string },
|
|
13
|
+
* instructions: {
|
|
14
|
+
* source: 'inline'|'local_file'|'package_file'|'database'|'object_store',
|
|
15
|
+
* ref?: string,
|
|
16
|
+
* inline?: string,
|
|
17
|
+
* },
|
|
18
|
+
* execution?: {
|
|
19
|
+
* mode?: 'turn',
|
|
20
|
+
* tools?: string[],
|
|
21
|
+
* operations?: string[],
|
|
22
|
+
* capabilities?: string[],
|
|
23
|
+
* },
|
|
24
|
+
* }} SkillManifest
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @param {unknown} raw
|
|
29
|
+
* @returns {SkillManifest}
|
|
30
|
+
*/
|
|
31
|
+
export function normalizeSkillManifest(raw) {
|
|
32
|
+
if (!raw || typeof raw !== 'object') throw new Error('skill_manifest_required');
|
|
33
|
+
const input = /** @type {Record<string, unknown>} */ (raw);
|
|
34
|
+
const id = String(input.id || '')
|
|
35
|
+
.trim()
|
|
36
|
+
.toLowerCase()
|
|
37
|
+
.replace(/_/g, '-');
|
|
38
|
+
if (!/^[a-z0-9][a-z0-9-]{0,63}$/.test(id)) throw new Error(`invalid_skill_id:${id}`);
|
|
39
|
+
|
|
40
|
+
const instructions = input.instructions && typeof input.instructions === 'object'
|
|
41
|
+
? /** @type {Record<string, unknown>} */ (input.instructions)
|
|
42
|
+
: null;
|
|
43
|
+
if (!instructions?.source) throw new Error('skill_instructions_source_required');
|
|
44
|
+
|
|
45
|
+
const source = String(instructions.source);
|
|
46
|
+
const allowed = new Set(['inline', 'local_file', 'package_file', 'database', 'object_store']);
|
|
47
|
+
if (!allowed.has(source)) throw new Error(`unsupported_instruction_source:${source}`);
|
|
48
|
+
|
|
49
|
+
let suggested;
|
|
50
|
+
const slash = input.slash && typeof input.slash === 'object'
|
|
51
|
+
? /** @type {Record<string, unknown>} */ (input.slash)
|
|
52
|
+
: null;
|
|
53
|
+
if (slash?.suggested) {
|
|
54
|
+
suggested = canonicalizeSlashTrigger(slash.suggested);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const execution = input.execution && typeof input.execution === 'object'
|
|
58
|
+
? /** @type {Record<string, unknown>} */ (input.execution)
|
|
59
|
+
: {};
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
schema: 'agentsam.skill.v1',
|
|
63
|
+
id,
|
|
64
|
+
name: String(input.name || id).trim() || id,
|
|
65
|
+
description: String(input.description || '').trim(),
|
|
66
|
+
icon: String(input.icon || 'skill').trim() || 'skill',
|
|
67
|
+
version: input.version != null ? String(input.version) : undefined,
|
|
68
|
+
package: input.package != null ? String(input.package) : undefined,
|
|
69
|
+
slash: suggested ? { suggested } : undefined,
|
|
70
|
+
instructions: {
|
|
71
|
+
source: /** @type {SkillManifest['instructions']['source']} */ (source),
|
|
72
|
+
ref: instructions.ref != null ? String(instructions.ref) : undefined,
|
|
73
|
+
inline: instructions.inline != null ? String(instructions.inline) : undefined,
|
|
74
|
+
},
|
|
75
|
+
execution: {
|
|
76
|
+
mode: 'turn',
|
|
77
|
+
tools: Array.isArray(execution.tools) ? execution.tools.map(String) : [],
|
|
78
|
+
operations: Array.isArray(execution.operations) ? execution.operations.map(String) : [],
|
|
79
|
+
capabilities: Array.isArray(execution.capabilities) ? execution.capabilities.map(String) : [],
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* @param {string} id
|
|
86
|
+
* @param {string} [inline]
|
|
87
|
+
* @returns {SkillManifest}
|
|
88
|
+
*/
|
|
89
|
+
export function createBlankSkillManifest(id, inline = '# New skill\n\nDescribe the playbook.\n') {
|
|
90
|
+
const normalized = String(id || '')
|
|
91
|
+
.trim()
|
|
92
|
+
.toLowerCase()
|
|
93
|
+
.replace(/_/g, '-');
|
|
94
|
+
return normalizeSkillManifest({
|
|
95
|
+
schema: 'agentsam.skill.v1',
|
|
96
|
+
id: normalized,
|
|
97
|
+
name: normalized,
|
|
98
|
+
description: '',
|
|
99
|
+
icon: 'skill',
|
|
100
|
+
slash: { suggested: `/${normalized}` },
|
|
101
|
+
instructions: { source: 'inline', inline },
|
|
102
|
+
execution: { mode: 'turn', tools: [], operations: [], capabilities: [] },
|
|
103
|
+
});
|
|
104
|
+
}
|