@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.
Files changed (233) hide show
  1. package/AGENTSAM.md +6 -0
  2. package/README.md +16 -1
  3. package/bin/agentsam +16 -1
  4. package/docs/BRAND_INTELLIGENCE.md +1 -1
  5. package/docs/architecture/AGENTSAM_DISTRIBUTION_OWNERSHIP.md +176 -0
  6. package/docs/architecture/AGENTSAM_GO_RUNTIME.md +282 -0
  7. package/docs/architecture/CODEBASEINDEX_GUIDED_PATH.md +202 -0
  8. package/docs/architecture/FS_E2E_CLOSURE_RECEIPT.md +59 -0
  9. package/docs/architecture/LOCAL_FS_AUTHORITY.md +38 -0
  10. package/docs/architecture/LOCAL_STUDIO_CLOUDFLARE_OAUTH.md +28 -0
  11. package/docs/architecture/PLAN_CLI_AND_LOCAL_STUDIO_DESKTOP.md +96 -0
  12. package/docs/architecture/SAM_ACTIVITY_RECOVERY_RECEIPT.md +39 -0
  13. package/docs/architecture/SAM_DECISION_WORK_RECEIPT.md +54 -0
  14. package/docs/architecture/SAM_KERNEL.md +181 -0
  15. package/docs/architecture/SAM_MACHINE_NORMALIZATION_PRECOMMIT_REPORT.md +208 -0
  16. package/docs/architecture/SLASH_SKILLS_PORTABLE.md +116 -0
  17. package/docs/architecture/fs-e2e-receipt.latest.json +42 -0
  18. package/docs/architecture/previews/codebaseindex-guided-path/CODEBASEINDEX_GUIDED_PATH.md +202 -0
  19. package/docs/architecture/previews/codebaseindex-guided-path/index.html +321 -0
  20. package/migrations/d1/0010_portable_tickets_memory.sql +140 -0
  21. package/migrations/d1/0011_agentsam_skill_v2.sql +185 -0
  22. package/migrations/d1/0011b_agentsam_skill_v2_cutover.sql +22 -0
  23. package/migrations/d1/0011c_agentsam_skill_v2_backfill.sql +76 -0
  24. package/migrations/d1/0011d_agentsam_skill_v2_retrieval_revisions.sql +50 -0
  25. package/migrations/d1/0012_agentsam_tools_required_seed.sql +67 -0
  26. package/migrations/d1/0013_identity_oauth_states.sql +15 -0
  27. package/migrations/d1/0014_auth_event_log.sql +20 -0
  28. package/migrations/d1/0015_identity_oauth_state_app_id.sql +3 -0
  29. package/migrations/d1/README_PORTABLE_CONTROL_PLANE.md +16 -0
  30. package/migrations/sqlite/agentsam_skill_retrieval.portable.sql +42 -0
  31. package/package.json +19 -4
  32. package/packages/agentsam-contracts/src/errors.ts +16 -0
  33. package/packages/agentsam-errors/src/envelope.js +53 -0
  34. package/packages/agentsam-errors/src/index.js +1 -0
  35. package/packages/agentsam-errors/src/recovery.js +301 -0
  36. package/packages/agentsam-knowledge/src/providers/index.js +18 -6
  37. package/packages/connectors/cloudflare/src/routes.js +9 -0
  38. package/packages/connectors/cloudflare/tests/connector.test.mjs +26 -1
  39. package/packages/identity/.agentsam/features/oauth-login-portal/agentsam.feature.json +1 -1
  40. package/packages/identity/.agentsam/features/oauth-login-portal/routes.json +11 -2
  41. package/packages/identity/docs/PORTABLE_IDENTITY_ARCHITECTURE.md +50 -0
  42. package/packages/identity/migrations/D1_SCHEMA_MAPPING.md +31 -0
  43. package/packages/identity/migrations/sqlite/001_identity_core.sql +99 -0
  44. package/packages/identity/migrations/sqlite/002_identity_oauth_client.sql +38 -0
  45. package/packages/identity/migrations/sqlite/003_identity_oauth_server.sql +64 -0
  46. package/packages/identity/package.json +2 -2
  47. package/packages/identity/src/adapters/cloudflare-d1/index.js +122 -22
  48. package/packages/identity/src/adapters/sqlite/index.js +319 -0
  49. package/packages/identity/src/app/verify-app.js +95 -0
  50. package/packages/identity/src/contracts/identity-store.js +115 -0
  51. package/packages/identity/src/contracts/route-ids.js +30 -0
  52. package/packages/identity/src/contracts/route-projection.js +218 -0
  53. package/packages/identity/src/contracts/routes.js +11 -0
  54. package/packages/identity/src/core/browser-paths.js +4 -5
  55. package/packages/identity/src/core/constants.js +13 -8
  56. package/packages/identity/src/core/session-policy.js +32 -0
  57. package/packages/identity/src/frontend/auth-portal/pages/login.html +10 -10
  58. package/packages/identity/src/frontend/auth-portal/pages/reset.html +3 -3
  59. package/packages/identity/src/frontend/auth-portal/pages/signup.html +3 -3
  60. package/packages/identity/src/frontend/auth-portal/preview/dashboard-stub.html +1 -1
  61. package/packages/identity/src/index.js +25 -0
  62. package/packages/identity/src/oauth/README.md +10 -4
  63. package/packages/identity/src/oauth/credentials.js +20 -13
  64. package/packages/identity/src/oauth/finalize-inbound.js +1 -1
  65. package/packages/identity/src/oauth/iam-platform.js +8 -7
  66. package/packages/identity/src/oauth/redirect-paths.js +27 -36
  67. package/packages/identity/src/server/identity-service.js +30 -13
  68. package/packages/identity/src/server/mount-policy.js +30 -0
  69. package/packages/identity/src/server/post-auth.js +79 -0
  70. package/packages/identity/src/server/worker-router.js +104 -72
  71. package/packages/identity/tests/finalize-inbound-oauth.test.mjs +6 -6
  72. package/packages/identity/tests/iam-provider.test.mjs +1 -1
  73. package/packages/identity/tests/identity-service.test.mjs +36 -5
  74. package/packages/identity/tests/oauth-credentials.test.mjs +3 -1
  75. package/packages/identity/tests/portable-identity-architecture.test.mjs +157 -0
  76. package/packages/identity/tests/session-routes-policy.test.mjs +21 -0
  77. package/packages/theme-church-site/package.json +2 -1
  78. package/packages/theme-church-site/src/index.js +1 -0
  79. package/packages/theme-companions-site/package.json +2 -1
  80. package/packages/theme-companions-site/src/index.js +1 -0
  81. package/packages/theme-floors-site/package.json +2 -1
  82. package/packages/theme-floors-site/src/index.js +1 -0
  83. package/packages/theme-fuelnfree-site/package.json +2 -1
  84. package/packages/theme-fuelnfree-site/src/index.js +1 -0
  85. package/packages/theme-handyman-site/package.json +2 -1
  86. package/packages/theme-handyman-site/src/index.js +1 -0
  87. package/packages/theme-insurance-site/package.json +2 -1
  88. package/packages/theme-insurance-site/src/index.js +1 -0
  89. package/packages/theme-shinshu-site/package.json +2 -1
  90. package/packages/theme-shinshu-site/src/index.js +1 -0
  91. package/protocol/apps/agentsam.app.v1.schema.json +51 -0
  92. package/protocol/brand/brandpack.v1.schema.json +43 -0
  93. package/protocol/credentials/issue.v1.schema.json +38 -0
  94. package/protocol/database/connection.v1.schema.json +41 -0
  95. package/protocol/embeddings/embedding-profile.v1.schema.json +20 -0
  96. package/protocol/errors/error-envelope.schema.json +135 -1
  97. package/protocol/errors/recovery.v1.schema.json +50 -0
  98. package/protocol/runtime/workspace-fs.v1.schema.json +71 -0
  99. package/protocol/sam/activity.v1.schema.json +48 -0
  100. package/protocol/sam/answer.v1.schema.json +35 -0
  101. package/protocol/sam/calibration.v1.schema.json +21 -0
  102. package/protocol/sam/decision-receipt.v1.schema.json +29 -0
  103. package/protocol/sam/evaluation.v1.schema.json +19 -0
  104. package/protocol/sam/operation.schema.json +66 -0
  105. package/protocol/sam/outcome.v1.schema.json +36 -0
  106. package/protocol/sam/question.v1.schema.json +26 -0
  107. package/protocol/sam/registry.seed.json +153 -0
  108. package/protocol/sam/result.schema.json +53 -0
  109. package/protocol/sam/state.v1.schema.json +23 -0
  110. package/protocol/skills/agentsam.interaction.v1.schema.json +50 -0
  111. package/protocol/skills/agentsam.skill.v1.schema.json +57 -0
  112. package/protocol/ui/icon-registry.mjs +226 -0
  113. package/protocol/ui/icon.v1.schema.json +51 -0
  114. package/skills/README.md +22 -9
  115. package/skills/agentsam-codebaseindex/SKILL.md +225 -0
  116. package/skills/catalog.json +14 -0
  117. package/src/cli/command-catalog.js +130 -0
  118. package/src/cli/dispatch.js +48 -0
  119. package/src/cli.js +43 -1
  120. package/src/commands/api-key.js +244 -0
  121. package/src/commands/app.js +60 -28
  122. package/src/commands/brand.js +17 -19
  123. package/src/commands/codebaseindex.js +688 -0
  124. package/src/commands/env.js +152 -25
  125. package/src/commands/go.js +366 -53
  126. package/src/commands/interaction-clack.js +115 -0
  127. package/src/commands/models.js +1 -1
  128. package/src/commands/providers.js +62 -14
  129. package/src/commands/shell.js +43 -2
  130. package/src/commands/skill.js +248 -0
  131. package/src/commands/skills.js +1 -1
  132. package/src/commands/start-local.js +4 -0
  133. package/src/commands/whoami.js +90 -18
  134. package/src/go/build.js +229 -39
  135. package/src/go/cloudflare.js +506 -105
  136. package/src/go/container.js +120 -0
  137. package/src/go/contract.js +9 -4
  138. package/src/go/discover.js +149 -33
  139. package/src/go/index.js +15 -3
  140. package/src/go/native-probe-runner.mjs +119 -0
  141. package/src/go/{registry.js → official-registry.js} +64 -7
  142. package/src/go/official-release.js +10 -0
  143. package/src/go/receipts.js +77 -10
  144. package/src/go/verify.js +9 -2
  145. package/src/index.js +27 -0
  146. package/src/indexing/ingest/discover-models.js +298 -0
  147. package/src/indexing/ingest/inventory.js +243 -0
  148. package/src/indexing/ingest/job-graph.js +181 -0
  149. package/src/indexing/ingest/materials.js +210 -0
  150. package/src/lib/provider-credentials.js +63 -21
  151. package/src/lib/slash-commands.js +1 -0
  152. package/src/local-fs/capability.js +121 -0
  153. package/src/local-fs/freshness.js +75 -0
  154. package/src/local-fs/index.js +385 -0
  155. package/src/local-fs/paths.js +100 -0
  156. package/src/local-pty/server.js +295 -19
  157. package/src/mcp/client.js +2 -2
  158. package/src/models/ai-access-onboarding.js +112 -0
  159. package/src/models/discovery.js +10 -2
  160. package/src/models/inventory-core.js +9 -1
  161. package/src/sam/activity/index.js +183 -0
  162. package/src/sam/client.js +252 -0
  163. package/src/sam/decision/calibration.js +109 -0
  164. package/src/sam/decision/confidence.js +126 -0
  165. package/src/sam/decision/evaluate.js +157 -0
  166. package/src/sam/decision/evaluators/deterministic.js +341 -0
  167. package/src/sam/decision/evaluators/heuristic.js +61 -0
  168. package/src/sam/decision/evaluators/select.js +50 -0
  169. package/src/sam/decision/evaluators/semantic.js +149 -0
  170. package/src/sam/decision/hierarchical.js +61 -0
  171. package/src/sam/decision/index.js +53 -0
  172. package/src/sam/decision/policy.js +86 -0
  173. package/src/sam/decision/questions.js +120 -0
  174. package/src/sam/decision/receipt.js +148 -0
  175. package/src/sam/decision/state.js +117 -0
  176. package/src/sam/decision/types.js +22 -0
  177. package/src/sam/decision/validate.js +200 -0
  178. package/src/sam/define.js +51 -0
  179. package/src/sam/index.js +65 -0
  180. package/src/sam/operations/brand-scan.js +62 -0
  181. package/src/sam/operations/cad-blender-inspect.js +36 -0
  182. package/src/sam/operations/codebaseindex-ingest.js +49 -0
  183. package/src/sam/operations/decision-evaluate.js +59 -0
  184. package/src/sam/operations/planning-astar.js +77 -0
  185. package/src/sam/operations/planning-goap.js +60 -0
  186. package/src/sam/operations/repository-inspect.js +72 -0
  187. package/src/sam/operations/security-scan.js +33 -0
  188. package/src/sam/operations/terminal-exec.js +29 -0
  189. package/src/sam/planning/astar.js +311 -0
  190. package/src/sam/planning/goap.js +177 -0
  191. package/src/sam/planning/index.js +21 -0
  192. package/src/sam/planning/state.js +84 -0
  193. package/src/sam/registry.js +48 -0
  194. package/src/sam/result.js +77 -0
  195. package/src/sam/seed.js +44 -0
  196. package/src/sam/types.js +91 -0
  197. package/src/skills/catalog.js +64 -0
  198. package/src/skills/content-resolver.js +124 -0
  199. package/src/skills/hosted-store.js +37 -0
  200. package/src/skills/index.js +29 -64
  201. package/src/skills/interaction.js +102 -0
  202. package/src/skills/local-store.js +228 -0
  203. package/src/skills/manifest.js +104 -0
  204. package/src/skills/metrics.js +31 -0
  205. package/src/skills/registry.js +184 -0
  206. package/src/skills/runtime.js +287 -0
  207. package/src/skills/slash.js +44 -0
  208. package/src/ui/cli/help.js +94 -101
  209. package/test/cli/api-key-env-whoami.test.mjs +129 -0
  210. package/test/cli/codebaseindex-plan-ux.test.mjs +37 -0
  211. package/test/cli/go.test.mjs +62 -5
  212. package/test/cli/skill-npm-and-env.test.mjs +52 -0
  213. package/test/cli/wireframes-go-registry.test.mjs +87 -2
  214. package/test/go/build-source-identity.test.mjs +31 -0
  215. package/test/go/cloudflare-probe.test.mjs +274 -10
  216. package/test/go/distribution.test.mjs +28 -0
  217. package/test/integration/ai-access-onboarding.test.mjs +47 -0
  218. package/test/integration/cli-help.test.mjs +1 -1
  219. package/test/integration/cms-site-tenancy-contract.test.mjs +6 -6
  220. package/test/integration/codebaseindex-ingest.test.mjs +166 -0
  221. package/test/integration/icon-registry.test.mjs +67 -0
  222. package/test/integration/ingest-discover-models.test.mjs +30 -0
  223. package/test/integration/install-script.test.mjs +12 -9
  224. package/test/integration/local-fs.test.mjs +113 -0
  225. package/test/integration/provider-env-cli.test.mjs +2 -1
  226. package/test/integration/sam-activity-recovery.test.mjs +147 -0
  227. package/test/integration/sam-decision.test.mjs +584 -0
  228. package/test/integration/sam-kernel.test.mjs +99 -0
  229. package/test/integration/sam-planning-astar.test.mjs +279 -0
  230. package/test/integration/skill-runtime.test.mjs +240 -0
  231. package/test/integration/studio-fs-pty-e2e.test.mjs +294 -0
  232. package/test/models.test.mjs +14 -7
  233. package/test/shell.test.mjs +4 -4
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Present AgentSamInteraction via Clack (or plain write fallback).
3
+ * Hosts (shell, Local Studio, Tauri) can swap the prompt impls.
4
+ */
5
+
6
+ import { confirm, isCancel, select, text } from '@clack/prompts';
7
+
8
+ /**
9
+ * @param {import('../skills/interaction.js').AgentSamInteraction} interaction
10
+ * @param {{
11
+ * write?: (s: string) => void,
12
+ * interactive?: boolean,
13
+ * selectImpl?: typeof select,
14
+ * confirmImpl?: typeof confirm,
15
+ * textImpl?: typeof text,
16
+ * }} [options]
17
+ * @returns {Promise<{ action: string|null, value: string|null, cancelled: boolean }>}
18
+ */
19
+ export async function presentAgentSamInteraction(interaction, options = {}) {
20
+ const write = options.write || ((s) => process.stdout.write(s));
21
+ const interactive =
22
+ options.interactive ?? Boolean(process.stdin.isTTY && process.stdout.isTTY);
23
+ const prompt = interaction?.prompt;
24
+
25
+ if (!prompt?.message) {
26
+ return { action: null, value: null, cancelled: false };
27
+ }
28
+
29
+ for (const line of String(prompt.message).split('\n')) {
30
+ write(` ${line}\n`);
31
+ }
32
+
33
+ if (!interactive) {
34
+ return { action: null, value: null, cancelled: false };
35
+ }
36
+
37
+ const status = interaction.status;
38
+
39
+ if (status === 'needs_input' && prompt.kind === 'select' && Array.isArray(prompt.options) && prompt.options.length) {
40
+ const answer = await (options.selectImpl || select)({
41
+ message: prompt.title || 'Choose',
42
+ options: prompt.options.map((opt) => ({
43
+ value: opt.value,
44
+ label: opt.label,
45
+ hint: opt.description,
46
+ })),
47
+ });
48
+ if (isCancel(answer)) return { action: null, value: null, cancelled: true };
49
+ return { action: String(answer), value: String(answer), cancelled: false };
50
+ }
51
+
52
+ if (status === 'needs_input' && prompt.kind === 'text') {
53
+ const answer = await (options.textImpl || text)({
54
+ message: prompt.title || prompt.message,
55
+ });
56
+ if (isCancel(answer)) return { action: null, value: null, cancelled: true };
57
+ return { action: 'text', value: String(answer), cancelled: false };
58
+ }
59
+
60
+ if (status === 'needs_approval' || (status === 'needs_input' && prompt.kind === 'confirm')) {
61
+ const answer = await (options.confirmImpl || confirm)({
62
+ message: prompt.title || prompt.message,
63
+ });
64
+ if (isCancel(answer)) return { action: null, value: null, cancelled: true };
65
+ return { action: answer ? 'confirm' : 'cancel', value: answer ? 'yes' : 'no', cancelled: false };
66
+ }
67
+
68
+ if (
69
+ (status === 'blocked' || status === 'needs_input') &&
70
+ Array.isArray(prompt.actions) &&
71
+ prompt.actions.length
72
+ ) {
73
+ const answer = await (options.selectImpl || select)({
74
+ message: prompt.title || 'Next',
75
+ options: prompt.actions.map((a) => ({ value: a.id, label: a.label })),
76
+ });
77
+ if (isCancel(answer)) return { action: null, value: null, cancelled: true };
78
+ return { action: String(answer), value: String(answer), cancelled: false };
79
+ }
80
+
81
+ return { action: null, value: null, cancelled: false };
82
+ }
83
+
84
+ /**
85
+ * Map shell skill interaction choices to follow-up CLI actions.
86
+ */
87
+ export async function handleSkillInteractionChoice(choice, state = {}) {
88
+ const write = state.write || ((s) => process.stdout.write(s));
89
+ const { runSkill } = await import('./skill.js');
90
+ if (!choice || choice.cancelled) return null;
91
+ if (choice.action === 'create' || choice.value === 'create') {
92
+ const id = 'my-first-skill';
93
+ write(`\n Running: agentsam skill create ${id}\n\n`);
94
+ return runSkill(['create', id], { write, home: state.home, env: state.env });
95
+ }
96
+ if (typeof choice.value === 'string' && choice.value.startsWith('create:')) {
97
+ const id = choice.value.slice('create:'.length) || 'my-first-skill';
98
+ write(`\n Running: agentsam skill create ${id}\n\n`);
99
+ return runSkill(['create', id], { write, home: state.home, env: state.env });
100
+ }
101
+ if (typeof choice.action === 'string' && choice.action.startsWith('create:')) {
102
+ const id = choice.action.slice('create:'.length) || 'my-first-skill';
103
+ write(`\n Running: agentsam skill create ${id}\n\n`);
104
+ return runSkill(['create', id], { write, home: state.home, env: state.env });
105
+ }
106
+ if (choice.action === 'install' || choice.value === 'install') {
107
+ write('\n Next: agentsam skill install ./path-to-skill (or @scope/pkg)\n\n');
108
+ return null;
109
+ }
110
+ if (choice.action === 'list' || choice.value === 'list') {
111
+ return runSkill(['list'], { write, home: state.home, env: state.env });
112
+ }
113
+ // create with prefilled id handled above
114
+ return null;
115
+ }
@@ -1,7 +1,7 @@
1
1
  import pc from 'picocolors';
2
2
  import { isCancel, select } from '@clack/prompts';
3
3
  import { probeOllama, resolveOllamaConfig } from './ollama.js';
4
- import { listModelCatalog } from '../models/index.js';
4
+ import { listModelCatalog } from '../models/catalog.js';
5
5
  import { discoverProviderModels } from '../models/discovery.js';
6
6
  import { resolveProviderCredential } from '../lib/provider-credentials.js';
7
7
  import { providerChoices, promptAndConfigureProvider } from './providers.js';
@@ -164,8 +164,13 @@ export async function validateAndSaveProviderCredential(provider, secret, option
164
164
  };
165
165
  }
166
166
 
167
- // Key is verified! Now persist securely to OS store + AES-256-GCM vault + env profile
168
- setProviderCredential(id, cleanSecret, { ...options, accountId });
167
+ // Key is verified. Persist securely to the OS store + AES-256-GCM vault.
168
+ // Plaintext shell profiles remain explicit opt-in only.
169
+ setProviderCredential(id, cleanSecret, {
170
+ ...options,
171
+ accountId,
172
+ writeEnvProfile: options.writeEnvProfile === true,
173
+ });
169
174
 
170
175
  return {
171
176
  attempted: true,
@@ -213,7 +218,11 @@ export async function promptAndConfigureProvider(provider, options = {}) {
213
218
  const spin = options.spinnerImpl ? options.spinnerImpl() : spinner();
214
219
  spin.start(`Verifying ${spec.label} API credential`);
215
220
 
216
- const result = await validateAndSaveProviderCredential(id, String(secret), { ...options, accountId });
221
+ const result = await validateAndSaveProviderCredential(id, String(secret), {
222
+ ...options,
223
+ accountId,
224
+ writeEnvProfile: options.writeEnvProfile === true,
225
+ });
217
226
 
218
227
  if (!result.ok) {
219
228
  spin.stop(`Verification failed: ${result.error || 'invalid key'} · Key was NOT saved`, 1);
@@ -255,10 +264,19 @@ async function runInteractiveProviders(options = {}) {
255
264
  }
256
265
 
257
266
  function parseArgs(argv = []) {
258
- const out = { command: 'interactive', provider: '', json: false, verify: false, yes: false, fromEnv: '' };
267
+ const out = {
268
+ command: 'interactive',
269
+ provider: '',
270
+ json: false,
271
+ verify: false,
272
+ yes: false,
273
+ fromEnv: '',
274
+ fromStdin: false,
275
+ exportProfile: true,
276
+ };
259
277
  const args = [...argv];
260
278
  if (args[0] && !args[0].startsWith('-')) out.command = args.shift();
261
- if (['add', 'set', 'remove', 'verify', 'status', 'export'].includes(out.command) && args[0] && !args[0].startsWith('-')) {
279
+ if (['add', 'set', 'remove', 'verify', 'status', 'export', 'roll'].includes(out.command) && args[0] && !args[0].startsWith('-')) {
262
280
  out.provider = normalizeProviderId(args.shift());
263
281
  }
264
282
  while (args.length) {
@@ -267,6 +285,9 @@ function parseArgs(argv = []) {
267
285
  else if (arg === '--verify') out.verify = true;
268
286
  else if (arg === '--yes' || arg === '-y') out.yes = true;
269
287
  else if (arg === '--from-env') out.fromEnv = clean(args.shift());
288
+ else if (arg === '--from-stdin') out.fromStdin = true;
289
+ else if (arg === '--no-export') out.exportProfile = false;
290
+ else if (arg === '--export') out.exportProfile = true;
270
291
  else if (arg === '--help' || arg === '-h') out.command = 'help';
271
292
  else throw new Error(`unknown providers option: ${arg}`);
272
293
  }
@@ -281,13 +302,17 @@ export async function runProviders(argv = [], options = {}) {
281
302
  write([
282
303
  'agentsam providers',
283
304
  'agentsam providers status [provider] [--verify] [--json]',
284
- 'agentsam providers add <provider> [--from-env NAME]',
305
+ 'agentsam providers add <provider> [--from-env NAME|--from-stdin] [--no-export]',
306
+ 'agentsam providers roll <provider> [--from-env NAME|--from-stdin] replace + export env.d',
285
307
  'agentsam providers export <provider>',
286
308
  'agentsam providers verify [provider] [--json]',
287
309
  'agentsam providers remove <provider> [--yes]',
288
310
  '',
289
311
  `Providers: ${PROVIDER_ORDER.join(', ')}`,
290
312
  '',
313
+ 'Boot load: source ~/.agentsam/load-agent-env.sh <provider…>',
314
+ ' agentsam env boot-line',
315
+ '',
291
316
  ].join('\n'));
292
317
  return;
293
318
  }
@@ -338,24 +363,47 @@ export async function runProviders(argv = [], options = {}) {
338
363
  return results;
339
364
  }
340
365
 
341
- if (parsed.command === 'add' || parsed.command === 'set') {
366
+ if (parsed.command === 'add' || parsed.command === 'set' || parsed.command === 'roll') {
342
367
  if (!parsed.provider) throw new Error('providers add requires a provider');
343
- if (parsed.fromEnv) {
344
- const value = clean((options.env || process.env)[parsed.fromEnv]);
345
- if (!value) throw new Error(`environment credential missing: ${parsed.fromEnv}`);
346
- const verified = await validateAndSaveProviderCredential(parsed.provider, value, options);
368
+ if (parsed.command === 'roll') {
369
+ removeProviderCredential(parsed.provider, options);
370
+ }
371
+ let secretValue = '';
372
+ if (parsed.fromStdin) {
373
+ const chunks = [];
374
+ for await (const chunk of options.stdin || process.stdin) chunks.push(chunk);
375
+ secretValue = Buffer.concat(chunks.map((c) => (Buffer.isBuffer(c) ? c : Buffer.from(c)))).toString('utf8').trim();
376
+ if (!secretValue) throw new Error('stdin credential missing');
377
+ } else if (parsed.fromEnv) {
378
+ secretValue = clean((options.env || process.env)[parsed.fromEnv]);
379
+ if (!secretValue) throw new Error(`environment credential missing: ${parsed.fromEnv}`);
380
+ }
381
+ if (secretValue) {
382
+ const verified = await validateAndSaveProviderCredential(parsed.provider, secretValue, {
383
+ ...options,
384
+ writeEnvProfile: parsed.exportProfile,
385
+ });
347
386
  if (!verified.ok) {
348
387
  throw new Error(`Provider verification failed: ${verified.error}. Credential was not saved.`);
349
388
  }
350
- writeLine(write, ` ${parsed.provider} verified · ${verified.model_count || 0} models`);
389
+ writeLine(write, ` ${parsed.provider} verified · saved${parsed.exportProfile ? ' · env.d exported' : ''}`);
390
+ if (parsed.exportProfile) {
391
+ writeLine(write, ` source ~/.agentsam/load-agent-env.sh ${parsed.provider}`);
392
+ }
351
393
  return verified;
352
394
  }
353
395
  if (options.interactive === false || !(options.interactive ?? Boolean(process.stdin.isTTY && process.stdout.isTTY))) {
354
- throw new Error('providers add requires an interactive terminal or --from-env NAME');
396
+ throw new Error('providers add requires an interactive terminal, --from-env NAME, or --from-stdin');
355
397
  }
356
- const result = await promptAndConfigureProvider(parsed.provider, options);
398
+ const result = await promptAndConfigureProvider(parsed.provider, {
399
+ ...options,
400
+ writeEnvProfile: parsed.exportProfile,
401
+ });
357
402
  if (!result) return null;
358
403
  writeLine(write, result.ok ? ` ${parsed.provider} verified` : ` ${parsed.provider} verification failed: ${result.error}`);
404
+ if (result.ok && parsed.exportProfile) {
405
+ writeLine(write, ` source ~/.agentsam/load-agent-env.sh ${parsed.provider}`);
406
+ }
359
407
  return result;
360
408
  }
361
409
 
@@ -20,7 +20,7 @@ import { createInlineActivity } from '../ui/cli/activity.js';
20
20
  import { createCliRuntimePresenter } from '../ui/cli/runtime-events.js';
21
21
  import { renderCliFooter, renderDiffPreview, renderUsagePanel } from '../ui/cli/footer.js';
22
22
  import { diagnosticFromError, renderDiagnosticError } from '../errors/index.js';
23
- import { getModelRecord, mergeModelReference } from '../models/index.js';
23
+ import { getModelRecord, mergeModelReference, listModelCatalog } from '../models/index.js';
24
24
  import { discoverProviderModels } from '../models/discovery.js';
25
25
  import { detectCliProject, findCliProjectRoot, readCliPreferences, updateCliPreferences } from '../lib/cli-preferences.js';
26
26
  import { buildContextEconomicsReport, renderContextEconomics } from './context-economics.js';
@@ -40,6 +40,11 @@ import { tryResolveGitContext } from '../../packages/agentsam-repository/src/git
40
40
  import { syncWorkspaceStateToD1, readWorkspaceStateFromD1 } from '../../packages/agentsam-repository/src/workspace-state.js';
41
41
  import { syncGitCommitsToD1 } from '../../packages/agentsam-repository/src/work-tracking.js';
42
42
  import { readGoapState, listGoapTickets, createGoapGoal, switchGoapGoal, closeGoapGoal, renderGoapStatus, renderGoapList, renderGoapGoal, renderGoapWhy, renderGoapPlan } from '../../packages/agentsam-repository/src/goap.js';
43
+ import { SkillRegistry, SkillRuntime, SkillContentResolver } from '../skills/index.js';
44
+ import {
45
+ presentAgentSamInteraction,
46
+ handleSkillInteractionChoice,
47
+ } from './interaction-clack.js';
43
48
 
44
49
  function writeLine(write, value = '') { write(`${value}\n`); }
45
50
 
@@ -1026,8 +1031,44 @@ export async function dispatchShellLine(line, state = {}) {
1026
1031
  await runInteractiveModelTurn(line, state);
1027
1032
  break;
1028
1033
  }
1034
+ {
1035
+ // Explicit slash → portable skill registry (no D1 / R2 / subagents).
1036
+ const skillRuntime = new SkillRuntime({
1037
+ registry: new SkillRegistry({ home: state.home }),
1038
+ contentResolver: new SkillContentResolver(),
1039
+ });
1040
+ const skillResult = await skillRuntime.invoke({ input: line });
1041
+ if (skillResult.matched) {
1042
+ const interactiveStatuses = new Set(['needs_input', 'needs_approval', 'blocked']);
1043
+ if (
1044
+ state.interactive &&
1045
+ interactiveStatuses.has(skillResult.interaction?.status) &&
1046
+ skillResult.interaction?.prompt
1047
+ ) {
1048
+ const choice = await presentAgentSamInteraction(skillResult.interaction, {
1049
+ write,
1050
+ interactive: state.interactive,
1051
+ });
1052
+ if (choice.action === 'create' || String(choice.value || '').startsWith('create')) {
1053
+ await handleSkillInteractionChoice(choice, { write, home: state.home });
1054
+ } else if (choice.action === 'list' || choice.value === 'list') {
1055
+ await handleSkillInteractionChoice(choice, { write, home: state.home });
1056
+ } else if (choice.action === 'install' || choice.value === 'install') {
1057
+ await handleSkillInteractionChoice(choice, { write, home: state.home });
1058
+ }
1059
+ } else if (skillResult.interaction?.prompt?.message) {
1060
+ for (const msgLine of String(skillResult.interaction.prompt.message).split('\n')) {
1061
+ writeLine(write, ` ${msgLine}`);
1062
+ }
1063
+ } else if (skillResult.modelInstructions?.content) {
1064
+ writeLine(write, ` skill ${skillResult.skill?.id} · ${skillResult.receipt?.checksum || 'resolved'}`);
1065
+ writeLine(write, ` (turn instructions ready · ${skillResult.tools?.length || 0} tools declared)`);
1066
+ }
1067
+ break;
1068
+ }
1069
+ }
1029
1070
  writeLine(write, ` Unknown Agent Sam command: ${command}`);
1030
- writeLine(write, ' Type / or /help for available commands.');
1071
+ writeLine(write, ' Type / or /help for available commands. Use /skills for installable slash skills.');
1031
1072
  return { handled: false, exit: false, cwd: state.cwd };
1032
1073
  }
1033
1074
  } catch (error) {
@@ -0,0 +1,248 @@
1
+ import fs from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import { spawnSync } from 'node:child_process';
5
+ import {
6
+ SkillRegistry,
7
+ SkillRuntime,
8
+ SkillContentResolver,
9
+ createUserSkill,
10
+ installSkillFromPath,
11
+ aliasSkill,
12
+ removeSkill,
13
+ readLocalRegistry,
14
+ } from '../skills/index.js';
15
+
16
+ function writeLine(write, value = '') {
17
+ write(`${value}\n`);
18
+ }
19
+
20
+ function help() {
21
+ return [
22
+ 'AgentSam · skill',
23
+ ' agentsam skill list',
24
+ ' agentsam skill create <id>',
25
+ ' agentsam skill inspect <id>',
26
+ ' agentsam skill edit <id> (prints path to edit)',
27
+ ' agentsam skill install <path|npm-package> [--alias /trigger]',
28
+ ' agentsam skill alias <id> </trigger>',
29
+ ' agentsam skill remove <id>',
30
+ ' agentsam skill publish <id> (prints package checklist; no network)',
31
+ ' agentsam skill invoke </trigger> [args...]',
32
+ '',
33
+ 'Slash triggers are local. Package manifests only suggest a trigger.',
34
+ 'Collisions require an explicit alias — nothing is globally reserved.',
35
+ '',
36
+ ].join('\n');
37
+ }
38
+
39
+ function looksLikeNpmPackage(source) {
40
+ const s = String(source || '').trim();
41
+ if (!s) return false;
42
+ if (s.startsWith('.') || s.startsWith('/') || s.includes('\\')) return false;
43
+ if (s.startsWith('@')) return true;
44
+ // bare package name (no path separators)
45
+ return !s.includes('/') && !s.endsWith('.json');
46
+ }
47
+
48
+ /**
49
+ * Download an npm package tarball and return the extracted package root
50
+ * that contains agentsam.skill.json (or package root for relative resolve).
51
+ */
52
+ export function resolveNpmSkillPackage(spec, options = {}) {
53
+ const spawn = options.spawnSyncImpl || spawnSync;
54
+ const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'agentsam-skill-npm-'));
55
+ const pack = spawn('npm', ['pack', spec, '--pack-destination', tmp], {
56
+ encoding: 'utf8',
57
+ cwd: options.cwd || process.cwd(),
58
+ env: options.env || process.env,
59
+ });
60
+ if ((pack.status ?? 1) !== 0) {
61
+ throw new Error(`npm_pack_failed:${spec}:${String(pack.stderr || pack.stdout || '').trim() || 'unknown'}`);
62
+ }
63
+ const tgzName = String(pack.stdout || '')
64
+ .trim()
65
+ .split(/\r?\n/)
66
+ .filter(Boolean)
67
+ .pop();
68
+ if (!tgzName) throw new Error(`npm_pack_empty:${spec}`);
69
+ const tgzPath = path.join(tmp, path.basename(tgzName));
70
+ if (!fs.existsSync(tgzPath)) throw new Error(`npm_pack_missing:${tgzPath}`);
71
+
72
+ const extractDir = path.join(tmp, 'pkg');
73
+ fs.mkdirSync(extractDir, { recursive: true });
74
+ const tar = spawn('tar', ['-xzf', tgzPath, '-C', extractDir], { encoding: 'utf8' });
75
+ if ((tar.status ?? 1) !== 0) {
76
+ throw new Error(`npm_extract_failed:${String(tar.stderr || '').trim() || 'tar failed'}`);
77
+ }
78
+ const packageRoot = path.join(extractDir, 'package');
79
+ const candidates = [
80
+ path.join(packageRoot, 'agentsam.skill.json'),
81
+ path.join(packageRoot, 'skill', 'agentsam.skill.json'),
82
+ path.join(packageRoot, 'skills', 'agentsam.skill.json'),
83
+ ];
84
+ for (const file of candidates) {
85
+ if (fs.existsSync(file)) return path.dirname(file);
86
+ }
87
+ // walk one level
88
+ if (fs.existsSync(packageRoot)) {
89
+ for (const name of fs.readdirSync(packageRoot)) {
90
+ const nested = path.join(packageRoot, name, 'agentsam.skill.json');
91
+ if (fs.existsSync(nested)) return path.dirname(nested);
92
+ }
93
+ }
94
+ throw new Error(`agentsam.skill.json_not_found_in_package:${spec}`);
95
+ }
96
+
97
+ /**
98
+ * @param {string[]} argv
99
+ * @param {{ write?: Function, home?: string, env?: NodeJS.ProcessEnv }} [options]
100
+ */
101
+ export async function runSkill(argv = [], options = {}) {
102
+ const write = options.write || ((t) => process.stdout.write(t));
103
+ const [command = 'list', ...rest] = argv;
104
+
105
+ if (command === '--help' || command === '-h' || command === 'help') {
106
+ write(help());
107
+ return null;
108
+ }
109
+
110
+ const registry = new SkillRegistry({ home: options.home, env: options.env });
111
+ const runtime = new SkillRuntime({
112
+ registry,
113
+ contentResolver: new SkillContentResolver(),
114
+ });
115
+
116
+ if (command === 'list') {
117
+ const rows = registry.list();
118
+ writeLine(write, '');
119
+ writeLine(write, ' AgentSam · skills');
120
+ writeLine(write, '');
121
+ for (const row of rows) {
122
+ const trig = row.trigger ? row.trigger.padEnd(22) : '(no trigger)'.padEnd(22);
123
+ writeLine(write, ` ${trig} ${row.id.padEnd(28)} [${row.source}]`);
124
+ if (row.description) writeLine(write, ` ${row.description}`);
125
+ }
126
+ writeLine(write, '');
127
+ return rows;
128
+ }
129
+
130
+ if (command === 'create') {
131
+ const id = rest[0];
132
+ if (!id) throw new Error('skill create requires <id>');
133
+ const result = createUserSkill(id, { home: options.home, env: options.env });
134
+ writeLine(write, '');
135
+ writeLine(write, ` created ${result.manifest.id}`);
136
+ writeLine(write, ` trigger ${result.trigger}`);
137
+ writeLine(write, ` path ${result.root}`);
138
+ writeLine(write, '');
139
+ return result;
140
+ }
141
+
142
+ if (command === 'inspect') {
143
+ const id = rest[0];
144
+ if (!id) throw new Error('skill inspect requires <id>');
145
+ const skill = registry.getById(id);
146
+ if (!skill) throw new Error(`unknown_skill:${id}`);
147
+ write(`${JSON.stringify(skill, null, 2)}\n`);
148
+ return skill;
149
+ }
150
+
151
+ if (command === 'edit') {
152
+ const id = rest[0];
153
+ if (!id) throw new Error('skill edit requires <id>');
154
+ const local = readLocalRegistry({ home: options.home, env: options.env });
155
+ const entry = local.entries[id];
156
+ if (!entry) throw new Error(`skill_not_user_editable:${id}`);
157
+ writeLine(write, entry.manifestPath);
158
+ writeLine(write, path.join(entry.baseDir, 'SKILL.md'));
159
+ return entry;
160
+ }
161
+
162
+ if (command === 'install') {
163
+ const source = rest[0];
164
+ if (!source) throw new Error('skill install requires <path|npm-package>');
165
+ let aliasOnCollision;
166
+ for (let i = 1; i < rest.length; i += 1) {
167
+ if (rest[i] === '--alias' || rest[i] === '--trigger') aliasOnCollision = rest[++i];
168
+ }
169
+
170
+ let installPath = source;
171
+ if (looksLikeNpmPackage(source)) {
172
+ writeLine(write, ` packing ${source}…`);
173
+ installPath = resolveNpmSkillPackage(source, options);
174
+ }
175
+
176
+ try {
177
+ const result = installSkillFromPath(installPath, {
178
+ home: options.home,
179
+ env: options.env,
180
+ aliasOnCollision,
181
+ });
182
+ writeLine(write, '');
183
+ writeLine(write, ` installed ${result.id}`);
184
+ writeLine(write, ` trigger ${result.trigger}`);
185
+ writeLine(write, '');
186
+ return result;
187
+ } catch (error) {
188
+ if (error?.code === 'SLASH_COLLISION') {
189
+ writeLine(write, '');
190
+ writeLine(write, ` Conflict: ${error.suggested} already owned by ${error.owner}`);
191
+ writeLine(write, ' Choose an alias:');
192
+ for (const c of error.candidates || []) {
193
+ writeLine(write, ` agentsam skill install ${source} --alias ${c}`);
194
+ }
195
+ writeLine(write, '');
196
+ throw error;
197
+ }
198
+ throw error;
199
+ }
200
+ }
201
+
202
+ if (command === 'alias') {
203
+ const id = rest[0];
204
+ const trigger = rest[1];
205
+ if (!id || !trigger) throw new Error('skill alias requires <id> </trigger>');
206
+ const result = aliasSkill(id, trigger, { home: options.home, env: options.env });
207
+ writeLine(write, ` ${result.id} → ${result.trigger}`);
208
+ return result;
209
+ }
210
+
211
+ if (command === 'remove') {
212
+ const id = rest[0];
213
+ if (!id) throw new Error('skill remove requires <id>');
214
+ const result = removeSkill(id, { home: options.home, env: options.env });
215
+ writeLine(write, result.removed ? ` removed ${id}` : ` ${id} was not installed`);
216
+ return result;
217
+ }
218
+
219
+ if (command === 'publish') {
220
+ const id = rest[0];
221
+ if (!id) throw new Error('skill publish requires <id>');
222
+ const skill = registry.getById(id);
223
+ if (!skill) throw new Error(`unknown_skill:${id}`);
224
+ writeLine(write, '');
225
+ writeLine(write, ' Publish checklist (no network from portable core):');
226
+ writeLine(write, ' 1. Ensure agentsam.skill.json validates as agentsam.skill.v1');
227
+ writeLine(write, ' 2. npm publish (suggested slash is not globally reserved)');
228
+ writeLine(write, ' 3. Consumers: agentsam skill install <path|@scope/pkg>');
229
+ writeLine(write, ` skill ${skill.id}`);
230
+ writeLine(write, ` slash ${skill.manifest.slash?.suggested || '(none)'}`);
231
+ writeLine(write, '');
232
+ return { id: skill.id, checklist: true };
233
+ }
234
+
235
+ if (command === 'invoke') {
236
+ const input = rest.join(' ').trim();
237
+ if (!input.startsWith('/')) throw new Error('skill invoke requires a /trigger');
238
+ const result = await runtime.invoke({ input });
239
+ write(`${JSON.stringify(result, null, 2)}\n`);
240
+ return result;
241
+ }
242
+
243
+ throw new Error(`unknown skill command: ${command}`);
244
+ }
245
+
246
+ export function skillCommandHelp() {
247
+ return help();
248
+ }
@@ -1,4 +1,4 @@
1
- import { getSkill, listSkills, loadSkill } from '../skills/index.js';
1
+ import { getSkill, listSkills, loadSkill } from '../skills/catalog.js';
2
2
 
3
3
  function parse(argv = []) {
4
4
  const out = { id: '', json: false, references: false };
@@ -45,6 +45,9 @@ export async function runStartLocal(opts = {}) {
45
45
  console.log(` ✓ PTY listening ${server.url}`);
46
46
  console.log(` ✓ Health ${server.healthUrl}`);
47
47
  console.log(` ✓ Project root ${server.cwd}`);
48
+ console.log(` ✓ Workspace ${server.workspace_id}`);
49
+ console.log(` ✓ Capability ${server.capability.capability.slice(0, 12)}… (loopback bootstrap)`);
50
+ console.log(` ✓ Runtime file ${server.runtimeFile}`);
48
51
  console.log(` ✓ Shell ${server.shell}`);
49
52
  console.log(`
50
53
  Project services:
@@ -52,6 +55,7 @@ export async function runStartLocal(opts = {}) {
52
55
  npm run db:status → inspect local SQLite
53
56
  npx agentsam → enter Agent Sam
54
57
 
58
+ Studio: open a filesystem workspace rooted at this same directory.
55
59
  Press Ctrl+C to stop.
56
60
  `);
57
61