@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
@@ -1,87 +1,19 @@
1
1
  import pc from 'picocolors';
2
2
  import { isCancel, select } from '@clack/prompts';
3
3
  import pkg from '../../../package.json' with { type: 'json' };
4
-
5
- const HELP_TOPICS = Object.freeze([
6
- {
7
- id: 'start',
8
- label: 'Start / resume',
9
- summary: 'Enter Agent Sam, resume work, inspect identity, and choose a model.',
10
- rows: [
11
- ['agentsam', 'Enter the interactive Agent Sam experience'],
12
- ['agentsam resume [session]', 'Resume a saved Agent Sam session'],
13
- ['agentsam whoami', 'Show authenticated account and credential status'],
14
- ['agentsam models', 'Probe account-visible hosted/local models'],
15
- ['agentsam providers', 'Configure and verify machine provider credentials'],
16
- ['agentsam env init <provider>', 'Low-level provider profile compatibility command'],
17
- ],
18
- },
19
- {
20
- id: 'work',
21
- label: 'Build / inspect',
22
- summary: 'Repository intelligence and normal project work.',
23
- rows: [
24
- ['agentsam inspect', 'Bounded repository index / authority view'],
25
- ['agentsam index', 'Incremental AST and optional embeddings'],
26
- ['agentsam search "query"', 'Search indexed code/text'],
27
- ['agentsam repo snapshot', 'Git composition/churn snapshot'],
28
- ['agentsam security', 'Dependency and trust-boundary scan'],
29
- ['agentsam merkle', 'Integrity snapshots and comparisons'],
30
- ],
31
- },
32
- {
33
- id: 'runtime',
34
- label: 'Runtime / terminal',
35
- summary: 'Local, remote, sandbox, and deployment controls.',
36
- rows: [
37
- ['agentsam status', 'Honest awareness (account, models, terminal, live deploy) · -i menu'],
38
- ['agentsam start-local', 'Start the local PTY service'],
39
- ['agentsam connections', 'Inspect available execution connections'],
40
- ['agentsam tunnel', 'List, inspect, and run real Cloudflare Tunnels through Wrangler'],
41
- ['agentsam mcp <add|status|doctor|list|remove>', 'Manage MCP connections, server catalogs, and client adapters'],
42
- ['agentsam eval <context|live>', 'Deterministic context scoring or live agent evaluation telemetry'],
43
- ['agentsam deploy', 'Graduate intentionally to cloud infrastructure'],
44
- ['agentsam cloudflare', 'Wrangler and Cloudflare runtime diagnostics'],
45
- ['agentsam go', 'Go runtime discovery, build, Cloudflare container deploy, D1 registry'],
46
- ],
47
- },
48
- {
49
- id: 'inside',
50
- label: 'Inside Agent Sam',
51
- summary: 'Commands available while the interactive session is running.',
52
- rows: [
53
- ['/', 'Open the keyboard command picker'],
54
- ['/model', 'Choose model, reasoning, and processing tier'],
55
- ['/providers', 'Configure and verify machine provider credentials'],
56
- ['/context', 'Show live context economics'],
57
- ['/usage', 'Show token/cost/session receipt'],
58
- ['/settings', 'Change runtime, terminal, and model policy'],
59
- ['/status', 'Honest project awareness + next-step tips'],
60
- ['/connections', 'Inspect live account, terminal, Worker, and Cloudflare connection evidence'],
61
- ['/tunnel', 'Choose an existing Cloudflare Tunnel and inspect or run it through Wrangler'],
62
- ['/help [topic]', 'Show in-session help'],
63
- ['/exit', 'Return to the host shell'],
64
- ],
65
- },
66
- {
67
- id: 'create',
68
- label: 'Create / extend',
69
- summary: 'Scaffold and add reusable capabilities.',
70
- rows: [
71
- ['agentsam create <name> --preset <preset>', 'Create a new AgentSam project'],
72
- ['agentsam add <capability>', 'Add a supported capability'],
73
- ['agentsam capabilities [id]', 'Inspect capability contracts'],
74
- ['agentsam skills [id]', 'Inspect packaged skills'],
75
- ['agentsam identity init', 'Add reusable identity surfaces'],
76
- ['agentsam scaffold <cms|worker-api>', 'Generate a Cloudflare starter project'],
77
- ['agentsam dockerize', 'Build supported container targets'],
78
- ],
79
- },
80
- ]);
4
+ import {
5
+ CLI_COMMAND_CATALOG,
6
+ CLI_HELP_TOPICS,
7
+ getCliCommand,
8
+ listCliCommands,
9
+ printAssistTip,
10
+ } from '../../cli/command-catalog.js';
11
+ import { SLASH_COMMANDS } from '../../lib/slash-commands.js';
81
12
 
82
13
  const TOPIC_ALIASES = new Map([
83
14
  ['models', 'start'], ['model', 'start'], ['provider', 'start'], ['providers', 'start'], ['resume', 'start'],
84
15
  ['repo', 'work'], ['repository', 'work'], ['inspect', 'work'], ['index', 'work'], ['security', 'work'],
16
+ ['ingest', 'work'], ['codebaseindex', 'work'], ['codebase-index', 'work'],
85
17
  ['terminal', 'runtime'], ['connections', 'runtime'], ['remote', 'runtime'], ['sandbox', 'runtime'], ['deploy', 'runtime'],
86
18
  ['mcp', 'runtime'], ['eval', 'runtime'],
87
19
  ['slash', 'inside'], ['commands', 'inside'], ['session', 'inside'], ['usage', 'inside'], ['context', 'inside'],
@@ -92,44 +24,66 @@ function clean(value) {
92
24
  return value == null ? '' : String(value).trim().toLowerCase();
93
25
  }
94
26
 
27
+ function commandsForTopic(topicId) {
28
+ return listCliCommands({ topic: topicId }).map((entry) => {
29
+ const aliases = (entry.aliases || []).filter((a) => !a.startsWith('-')).slice(0, 2);
30
+ const name = aliases.length ? `agentsam ${entry.id}|${aliases.join('|')}` : `agentsam ${entry.id}`;
31
+ const tip = entry.skill ? ` · skill ${entry.skill}` : '';
32
+ return [name, `${entry.summary}${tip}`];
33
+ });
34
+ }
35
+
95
36
  function padRows(rows) {
96
- const width = Math.min(42, Math.max(...rows.map(([command]) => command.length), 0));
37
+ const width = Math.min(52, Math.max(...rows.map(([command]) => command.length), 0));
97
38
  return rows.map(([command, description]) => ' ' + pc.cyan(command.padEnd(width)) + ' ' + pc.dim(description));
98
39
  }
99
40
 
100
41
  export function resolveHelpTopic(value) {
101
42
  const query = clean(value);
102
43
  if (!query) return null;
103
- const direct = HELP_TOPICS.find((topic) => topic.id === query || clean(topic.label) === query);
104
- if (direct) return direct;
44
+ const direct = CLI_HELP_TOPICS.find((topic) => topic.id === query || clean(topic.label) === query);
45
+ if (direct) {
46
+ return { ...direct, rows: commandsForTopic(direct.id) };
47
+ }
105
48
  const alias = TOPIC_ALIASES.get(query);
106
- if (alias) return HELP_TOPICS.find((topic) => topic.id === alias) || null;
107
- return HELP_TOPICS.find((topic) =>
108
- topic.rows.some(([command, description]) => clean(command).includes(query) || clean(description).includes(query))) || null;
49
+ if (alias) {
50
+ const topic = CLI_HELP_TOPICS.find((t) => t.id === alias);
51
+ return topic ? { ...topic, rows: commandsForTopic(topic.id) } : null;
52
+ }
53
+ const command = getCliCommand(query);
54
+ if (command) {
55
+ const topic = CLI_HELP_TOPICS.find((t) => t.id === command.topic);
56
+ return topic ? { ...topic, rows: commandsForTopic(topic.id) } : null;
57
+ }
58
+ return CLI_HELP_TOPICS.map((topic) => ({ ...topic, rows: commandsForTopic(topic.id) }))
59
+ .find((topic) => topic.rows.some(([cmd, description]) => clean(cmd).includes(query) || clean(description).includes(query))) || null;
109
60
  }
110
61
 
111
62
  export function renderHelpOverview(version, options = {}) {
63
+ const common = listCliCommands({ common: true });
112
64
  const lines = [
113
65
  '',
114
66
  ' ' + pc.bold('Agent Sam') + ' ' + pc.dim('v' + version),
115
- ' ' + pc.dim('Type normally to work with Agent Sam. Use help only when you need the map.'),
67
+ ' ' + pc.dim('SAM = Systematic Autonomous Machinery · help is generated from the command catalog.'),
116
68
  '',
117
69
  ' ' + pc.bold('Start'),
118
70
  ' ' + pc.cyan('agentsam') + ' ' + pc.dim('enter the interactive experience'),
119
71
  ' ' + pc.cyan('agentsam resume') + ' ' + pc.dim('continue saved work'),
120
- ' ' + pc.cyan('agentsam help <topic>') + ' ' + pc.dim('focused help'),
72
+ ' ' + pc.cyan('agentsam help <topic>') + ' ' + pc.dim('focused help from catalog'),
73
+ ' ' + pc.cyan('agentsam skills <id>') + ' ' + pc.dim('load how-to skill instructions'),
121
74
  '',
122
75
  ' ' + pc.bold('Common'),
123
- ' ' + pc.cyan('agentsam inspect') + ' ' + pc.dim('understand this repository'),
124
- ' ' + pc.cyan('agentsam models') + ' ' + pc.dim('see account-visible models'),
125
- ' ' + pc.cyan('agentsam status') + ' ' + pc.dim('check project/runtime health'),
126
- ' ' + pc.cyan('agentsam security') + ' ' + pc.dim('scan dependency + trust boundaries'),
127
- ' ' + pc.cyan('agentsam deploy') + ' ' + pc.dim('graduate intentionally'),
128
- '',
129
- ' ' + pc.dim('Inside Agent Sam: press / for the command picker. Ask the selected model for natural-language help at any time.'),
130
76
  ];
77
+ for (const entry of common) {
78
+ lines.push(' ' + pc.cyan(('agentsam ' + entry.id).padEnd(34)) + ' ' + pc.dim(entry.summary));
79
+ }
80
+ lines.push(
81
+ '',
82
+ ' ' + pc.dim('Every command prints: tip: use skill <id> — machine baseline for in-CLI guidance.'),
83
+ ' ' + pc.dim('Inside Agent Sam: press / for the command picker.'),
84
+ );
131
85
  if (options.showTopics !== false) {
132
- lines.push('', ' ' + pc.dim('Topics: ' + HELP_TOPICS.map((topic) => topic.id).join(' · ') + ' · all'));
86
+ lines.push('', ' ' + pc.dim('Topics: ' + CLI_HELP_TOPICS.map((topic) => topic.id).join(' · ') + ' · all · skills'));
133
87
  }
134
88
  lines.push('');
135
89
  return lines.join('\n');
@@ -137,21 +91,37 @@ export function renderHelpOverview(version, options = {}) {
137
91
 
138
92
  export function renderHelpTopic(topic, version) {
139
93
  if (!topic) return renderHelpOverview(version);
94
+ const skillHint = topic.rows?.[0]?.[1]?.includes('skill')
95
+ ? ''
96
+ : '';
140
97
  return [
141
98
  '',
142
99
  ' ' + pc.bold('Agent Sam') + ' ' + pc.dim('v' + version) + ' ' + pc.dim('·') + ' ' + pc.bold(topic.label),
143
100
  ' ' + pc.dim(topic.summary),
144
101
  '',
145
- ...padRows(topic.rows),
102
+ ...padRows(topic.rows || []),
146
103
  '',
147
- ' ' + pc.dim('Tip: run agentsam help for the map, or agentsam to return to the interactive experience.'),
104
+ ' ' + pc.dim('Tip: agentsam skills <id> loads how-to instructions for that command’s skill.'),
105
+ skillHint,
148
106
  '',
149
107
  ].join('\n');
150
108
  }
151
109
 
152
110
  export function renderAllHelp(version) {
153
111
  const lines = [renderHelpOverview(version, { showTopics: false })];
154
- for (const topic of HELP_TOPICS) lines.push(renderHelpTopic(topic, version));
112
+ for (const topic of CLI_HELP_TOPICS) {
113
+ lines.push(renderHelpTopic({ ...topic, rows: commandsForTopic(topic.id) }, version));
114
+ }
115
+ lines.push(
116
+ '',
117
+ ' ' + pc.bold('Inside shell (slash)'),
118
+ ...padRows(SLASH_COMMANDS.slice(0, 16).map((row) => [row.cmd, row.description])),
119
+ ' ' + pc.dim(`… ${SLASH_COMMANDS.length} slash commands total · /help inside the shell`),
120
+ '',
121
+ ' ' + pc.bold('Catalog'),
122
+ ' ' + pc.dim(`${CLI_COMMAND_CATALOG.length} top-level commands · source: src/cli/command-catalog.js`),
123
+ '',
124
+ );
155
125
  return lines.join('');
156
126
  }
157
127
 
@@ -160,11 +130,27 @@ export async function runHelp(argv = [], options = {}) {
160
130
  const write = options.write || ((value) => process.stdout.write(value));
161
131
  const args = argv.filter((arg) => arg !== '--interactive');
162
132
 
133
+ printAssistTip('help', { write: (s) => process.stderr.write(s) });
134
+
163
135
  if (args.includes('--all') || args.some((arg) => clean(arg) === 'all')) {
164
136
  write(renderAllHelp(version));
165
137
  return;
166
138
  }
167
139
 
140
+ if (args.some((arg) => clean(arg) === 'skills' || clean(arg) === 'skill')) {
141
+ write([
142
+ '',
143
+ ' ' + pc.bold('Skills') + ' ' + pc.dim('· how-to instructions per surface'),
144
+ ' ' + pc.cyan('agentsam skills') + ' ' + pc.dim('list portable skills'),
145
+ ' ' + pc.cyan('agentsam skills <id>') + ' ' + pc.dim('print skill instructions'),
146
+ ' ' + pc.cyan('agentsam skills <id> --references') + ' ' + pc.dim('include reference docs'),
147
+ '',
148
+ ' ' + pc.dim('Every CLI command tip points at a skill id — that is the machine baseline.'),
149
+ '',
150
+ ].join('\n'));
151
+ return;
152
+ }
153
+
168
154
  const topicArg = args.find((arg) => !arg.startsWith('-'));
169
155
  if (topicArg) {
170
156
  const topic = resolveHelpTopic(topicArg);
@@ -184,18 +170,25 @@ export async function runHelp(argv = [], options = {}) {
184
170
  }
185
171
 
186
172
  const choice = await select({
187
- message: 'Agent Sam help',
173
+ message: 'Agent Sam help (catalog-driven)',
188
174
  options: [
189
- ...HELP_TOPICS.map((topic) => ({ value: topic.id, label: topic.label, hint: topic.summary })),
190
- { value: 'all', label: 'All commands', hint: 'Print the complete deterministic help map' },
175
+ ...CLI_HELP_TOPICS.map((topic) => ({ value: topic.id, label: topic.label, hint: topic.summary })),
176
+ { value: 'skills', label: 'Skills', hint: 'How-to instructions loaded via agentsam skills' },
177
+ { value: 'all', label: 'All commands', hint: 'Print the complete catalog map' },
191
178
  { value: 'exit', label: 'Back', hint: 'Return without printing more help' },
192
179
  ],
193
180
  });
194
181
  if (isCancel(choice) || choice === 'exit') return;
195
182
  if (choice === 'all') write(renderAllHelp(version));
196
- else write(renderHelpTopic(resolveHelpTopic(choice), version));
183
+ else if (choice === 'skills') {
184
+ write([
185
+ '',
186
+ ' Run ' + pc.cyan('agentsam skills') + ' to list, or ' + pc.cyan('agentsam skills agentsam-codebaseindex') + ' for ingest how-to.',
187
+ '',
188
+ ].join('\n'));
189
+ } else write(renderHelpTopic(resolveHelpTopic(choice), version));
197
190
  }
198
191
 
199
192
  export function listHelpTopics() {
200
- return HELP_TOPICS.map((topic) => ({ id: topic.id, label: topic.label, summary: topic.summary }));
193
+ return CLI_HELP_TOPICS.map((topic) => ({ id: topic.id, label: topic.label, summary: topic.summary }));
201
194
  }
@@ -0,0 +1,129 @@
1
+ import assert from 'node:assert/strict';
2
+ import fs from 'node:fs';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import test from 'node:test';
6
+ import { runEnv } from '../../src/commands/env.js';
7
+ import { ensureAgentEnvLoader, renderEnvShellExports, setProviderCredential } from '../../src/lib/provider-credentials.js';
8
+ import { runApiKey } from '../../src/commands/api-key.js';
9
+ import { collectWhoami } from '../../src/commands/whoami.js';
10
+
11
+ function tmpHome() {
12
+ return fs.mkdtempSync(path.join(os.tmpdir(), 'agentsam-env-api-'));
13
+ }
14
+
15
+ test('load-agent-env.sh delegates to agentsam env shell (no plaintext secrets)', () => {
16
+ const home = tmpHome();
17
+ const loader = ensureAgentEnvLoader({ home });
18
+ const source = fs.readFileSync(loader, 'utf8');
19
+ assert.match(source, /agentsam env shell --profile default/);
20
+ assert.doesNotMatch(source, /export AGENTSAM_API_KEY=/);
21
+ assert.doesNotMatch(source, /env\.d\//);
22
+ });
23
+
24
+ test('env boot-line prints source load-agent-env without provider args', async () => {
25
+ const home = tmpHome();
26
+ const result = await runEnv(['boot-line'], { home, write: () => {} });
27
+ assert.equal(result.command, 'source ~/.agentsam/load-agent-env.sh');
28
+ });
29
+
30
+ test('env shell emits exports from vault without requiring ambient AGENTSAM_API_KEY', async () => {
31
+ const home = tmpHome();
32
+ setProviderCredential('inneranimalmedia', 'aak_testsecretvalue1234567890abcdef', {
33
+ home,
34
+ disableOsStore: true,
35
+ });
36
+ const result = await runEnv(['shell', '--profile', 'default'], {
37
+ home,
38
+ env: {},
39
+ write: () => {},
40
+ });
41
+ assert.match(result.script, /export AGENTSAM_API_KEY=/);
42
+ assert.ok(result.providers.includes('inneranimalmedia'));
43
+ });
44
+
45
+ test('renderEnvShellExports prefers vault over empty ambient env', () => {
46
+ const home = tmpHome();
47
+ setProviderCredential('cursor', 'cursor_test_key_value_xxxxxxxx', {
48
+ home,
49
+ disableOsStore: true,
50
+ });
51
+ const result = renderEnvShellExports({ home, env: {}, profile: 'default' });
52
+ assert.ok(result.providers.includes('cursor'));
53
+ assert.match(result.script, /export CURSOR_API_KEY=/);
54
+ });
55
+
56
+ test('api-key create stores secret via host mint response', async () => {
57
+ const home = tmpHome();
58
+ const result = await runApiKey(['create', '--name', 'Test Mac', '--store', 'vault', '--activate'], {
59
+ home,
60
+ env: {},
61
+ disableOsStore: true,
62
+ authorityLoader: async () => ({ value: 'oauth_session_token', kind: 'browser_oauth', source: 'test' }),
63
+ postJsonImpl: async () => ({
64
+ ok: true,
65
+ credential: { id: 'aakcred_1', name: 'Test Mac', prefix: 'aak_abc…', scopes: ['account:read'] },
66
+ secret_once: 'aak_mintedsecretvalue1234567890abcd',
67
+ }),
68
+ write: () => {},
69
+ });
70
+ assert.equal(result.ok, true);
71
+ assert.equal(result.activated, true);
72
+ assert.equal(result.credential.prefix, 'aak_abc…');
73
+ });
74
+
75
+ test('whoami surfaces tokenPermissions and authType from context', async () => {
76
+ const home = tmpHome();
77
+ const status = await collectWhoami({
78
+ home,
79
+ env: {},
80
+ authorityLoader: async () => ({ value: 'aak_presented', kind: 'api_key', source: 'environment' }),
81
+ contextLoader: async () => ({
82
+ user_id: 'au_1',
83
+ account_id: 'acct_1',
84
+ email: 'dev@example.test',
85
+ auth_type: 'api_key',
86
+ tokenPermissions: ['account:read', 'repository:read', 'models:invoke'],
87
+ credential: {
88
+ id: 'aakcred_1',
89
+ name: 'Sams-iMac',
90
+ prefix: 'aak_7fm…',
91
+ environment: 'development',
92
+ status: 'active',
93
+ },
94
+ cloudflare: { ok: true },
95
+ byok: {},
96
+ terminal: { available: false, instances: [], connections: [] },
97
+ }),
98
+ });
99
+ assert.equal(status.loggedIn, true);
100
+ assert.equal(status.authType, 'api_key');
101
+ assert.deepEqual(status.tokenPermissions, ['account:read', 'repository:read', 'models:invoke']);
102
+ assert.equal(status.credential.name, 'Sams-iMac');
103
+ assert.doesNotMatch(JSON.stringify(status), /aak_presented/);
104
+ });
105
+
106
+ test('repository does not normalize AGENTSAM_DEFAULT_APP', () => {
107
+ const root = path.resolve(path.dirname(new URL(import.meta.url).pathname), '../..');
108
+ const hits = [];
109
+ function walk(dir) {
110
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
111
+ if (entry.name === 'node_modules' || entry.name === '.git' || entry.name === 'dist') continue;
112
+ const full = path.join(dir, entry.name);
113
+ if (entry.isDirectory()) walk(full);
114
+ else if (/\.(js|mjs|ts|tsx|sh|json|md)$/.test(entry.name)) {
115
+ const text = fs.readFileSync(full, 'utf8');
116
+ // Allow docs that explicitly forbid the variable.
117
+ if (/AGENTSAM_DEFAULT_APP/.test(text) && !/no AGENTSAM_DEFAULT_APP|not normalize AGENTSAM_DEFAULT_APP|Remove.*AGENTSAM_DEFAULT_APP|doesNotMatch.*AGENTSAM_DEFAULT_APP|APP_SELECTOR=/.test(text)) {
118
+ if (/process\.env\.AGENTSAM_DEFAULT_APP|AGENTSAM_DEFAULT_APP\s*\|\||\$\{AGENTSAM_DEFAULT_APP/.test(text)) {
119
+ hits.push(full);
120
+ }
121
+ }
122
+ }
123
+ }
124
+ }
125
+ walk(path.join(root, 'src'));
126
+ walk(path.join(root, 'scripts'));
127
+ walk(path.join(root, 'apps/local-studio/backend'));
128
+ assert.deepEqual(hits, []);
129
+ });
@@ -0,0 +1,37 @@
1
+ import assert from 'node:assert/strict';
2
+ import { describe, it } from 'node:test';
3
+ import {
4
+ createCodebaseindexJobGraph,
5
+ advanceJobGraph,
6
+ freezePlanJobGraph,
7
+ formatJobGraphHuman,
8
+ } from '../../src/indexing/ingest/job-graph.js';
9
+ import { classifyTopLevel } from '../../src/indexing/ingest/inventory.js';
10
+
11
+ describe('codebaseindex job graph plan freeze', () => {
12
+ it('does not leave → run pointer after plan.dry_run', () => {
13
+ let g = createCodebaseindexJobGraph();
14
+ g = advanceJobGraph(g, 'plan.dry_run');
15
+ assert.equal(g.nodes.find((n) => n.status === 'run')?.id, 'ast.parse');
16
+ g = freezePlanJobGraph(g, { skipEmbedding: true });
17
+ assert.equal(g.status, 'planned');
18
+ assert.equal(g.nodes.filter((n) => n.status === 'run').length, 0);
19
+ assert.equal(g.nodes.find((n) => n.id === 'embedding.generate')?.status, 'skipped');
20
+ const text = formatJobGraphHuman(g, { planOnly: true });
21
+ assert.match(text, /PLANNED FOR RUN/);
22
+ assert.match(text, /○ ast\.parse/);
23
+ assert.doesNotMatch(text, /→/);
24
+ });
25
+ });
26
+
27
+ describe('inventory path categories', () => {
28
+ it('classifies source vs dependencies vs generated', () => {
29
+ assert.equal(classifyTopLevel('src'), 'source');
30
+ assert.equal(classifyTopLevel('apps'), 'source');
31
+ assert.equal(classifyTopLevel('docs'), 'docs');
32
+ assert.equal(classifyTopLevel('node_modules'), 'dependencies');
33
+ assert.equal(classifyTopLevel('dist'), 'dependencies');
34
+ assert.equal(classifyTopLevel('generated'), 'generated');
35
+ assert.equal(classifyTopLevel('.agentsam'), 'config');
36
+ });
37
+ });
@@ -9,6 +9,11 @@ import { runGo } from '../../src/commands/go.js';
9
9
 
10
10
  const SDK_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
11
11
 
12
+
13
+ function tempStateRoot() {
14
+ return fs.mkdtempSync(path.join(os.tmpdir(), 'agentsam-go-test-state-'));
15
+ }
16
+
12
17
  test('discoverGoRuntime finds agentsam-go-worker without inventing a sibling', () => {
13
18
  const discovery = discoverGoRuntime(SDK_ROOT);
14
19
  assert.equal(discovery.go.ok, true);
@@ -30,30 +35,75 @@ test('ensureProductContract refuses missing product roots instead of scaffolding
30
35
  test('buildGoProduct emits build receipt after go test/vet/build', () => {
31
36
  const discovery = discoverGoRuntime(SDK_ROOT);
32
37
  const productRoot = resolveProductRoot(discovery);
38
+ const stateRoot = tempStateRoot();
33
39
  const result = buildGoProduct({
34
40
  productRoot,
35
41
  runtimeRoot: discovery.runtime.runtimeRoot,
36
42
  repositoryRoot: discovery.repository_root,
43
+ stateRoot,
37
44
  });
38
45
  assert.equal(result.receipt.schema, 'agentsam.go-build-receipt.v1');
39
46
  assert.equal(result.tests.ok, true);
40
47
  assert.equal(result.vet.ok, true);
41
48
  assert.ok(result.build.binary);
49
+ assert.match(result.receipt.artifact.digest, /^sha256:[a-f0-9]{64}$/);
50
+ assert.equal(result.receipt.tests.runtime_probe, true);
51
+ assert.match(result.receipt.source.identity, /^git:[a-f0-9]{40}$/);
52
+ assert.equal(result.probe.checks.source_identity, true);
53
+ assert.equal(result.probe.checks.source_commit, true);
54
+ assert.equal(result.probe.checks.error_envelope, true);
55
+ assert.equal(result.probe.checks.clean_shutdown, true);
42
56
  assert.ok(fs.existsSync(result.receiptPath));
43
57
  });
44
58
 
59
+ test('agentsam go build --json exposes only portable build paths', async () => {
60
+ const stateRoot = tempStateRoot();
61
+ const chunks = [];
62
+
63
+ const result = await runGo(
64
+ ['build', '--json'],
65
+ {
66
+ write: (value) => chunks.push(String(value)),
67
+ stateRoot,
68
+ },
69
+ );
70
+
71
+ assert.ok(path.isAbsolute(result.receiptPath), 'internal execution keeps absolute receipt path');
72
+ assert.ok(path.isAbsolute(result.build.binary), 'internal execution keeps absolute binary path');
73
+
74
+ const output = chunks.join('');
75
+ const parsed = JSON.parse(output);
76
+
77
+ assert.equal(path.isAbsolute(parsed.receiptPath), false);
78
+ assert.equal(path.isAbsolute(parsed.build.binary), false);
79
+ assert.equal(path.isAbsolute(parsed.receipt.runtime.module_root), false);
80
+ assert.equal(Object.hasOwn(parsed.probe || {}, 'origin'), false);
81
+ assert.equal(Object.hasOwn(parsed.receipt?.probe || {}, 'origin'), false);
82
+ assert.equal(output.includes(SDK_ROOT), false);
83
+ });
84
+
45
85
  test('agentsam go --cloudflare agentsam-go-worker --skip-deploy is idempotent', async () => {
86
+ const stateRoot = tempStateRoot();
46
87
  const chunks = [];
47
88
  const write = (v) => chunks.push(String(v));
48
- const first = await runGo(['--cloudflare', 'agentsam-go-worker', '--skip-deploy', '--json'], { write });
89
+ const first = await runGo(
90
+ ['--cloudflare', 'agentsam-go-worker', '--skip-deploy', '--json'],
91
+ { write, stateRoot },
92
+ );
49
93
  assert.equal(first.ok, true);
50
94
  assert.equal(first.product, 'agentsam-go-worker');
51
95
  assert.equal(first.scaffold_changes_required, false);
96
+ assert.equal(first.mode, 'self_host');
97
+ assert.equal(first.deploy.registry.reason, 'self_host_registry_isolated');
52
98
 
53
99
  const chunks2 = [];
54
- const second = await runGo(['--cloudflare', 'agentsam-go-worker', '--skip-deploy', '--json'], {
55
- write: (v) => chunks2.push(String(v)),
56
- });
100
+ const second = await runGo(
101
+ ['--cloudflare', 'agentsam-go-worker', '--skip-deploy', '--json'],
102
+ {
103
+ write: (v) => chunks2.push(String(v)),
104
+ stateRoot,
105
+ },
106
+ );
57
107
  assert.equal(second.ok, true);
58
108
  assert.equal(second.existing_product, true);
59
109
  assert.equal(second.scaffold_changes_required, false);
@@ -62,8 +112,15 @@ test('agentsam go --cloudflare agentsam-go-worker --skip-deploy is idempotent',
62
112
  });
63
113
 
64
114
  test('agentsam go status --json reports discovery', async () => {
115
+ const stateRoot = tempStateRoot();
65
116
  const chunks = [];
66
- const result = await runGo(['status', '--json'], { write: (v) => chunks.push(String(v)) });
117
+ const result = await runGo(
118
+ ['status', '--json'],
119
+ {
120
+ write: (v) => chunks.push(String(v)),
121
+ stateRoot,
122
+ },
123
+ );
67
124
  assert.equal(result.ok, true);
68
125
  assert.ok(result.discovery.go.ok);
69
126
  const parsed = JSON.parse(chunks.join(''));
@@ -0,0 +1,52 @@
1
+ import assert from 'node:assert/strict';
2
+ import fs from 'node:fs';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import { spawnSync } from 'node:child_process';
6
+ import test from 'node:test';
7
+ import { resolveNpmSkillPackage } from '../../src/commands/skill.js';
8
+ import { installSkillFromPath } from '../../src/skills/index.js';
9
+ import { runEnv } from '../../src/commands/env.js';
10
+
11
+ function tmpHome() {
12
+ return fs.mkdtempSync(path.join(os.tmpdir(), 'agentsam-finish-'));
13
+ }
14
+
15
+ test('resolveNpmSkillPackage extracts agentsam.skill.json via npm pack', (t) => {
16
+ // Build a tiny local package and pack it by path (no registry).
17
+ const pkgRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'agentsam-npm-skill-'));
18
+ fs.writeFileSync(
19
+ path.join(pkgRoot, 'package.json'),
20
+ JSON.stringify({ name: 'tmp-agentsam-skill-fixture', version: '0.0.0', private: true }),
21
+ );
22
+ fs.writeFileSync(
23
+ path.join(pkgRoot, 'agentsam.skill.json'),
24
+ JSON.stringify({
25
+ schema: 'agentsam.skill.v1',
26
+ id: 'npm-fixture',
27
+ name: 'NPM Fixture',
28
+ slash: { suggested: '/npm-fixture' },
29
+ instructions: { source: 'inline', inline: '# npm fixture' },
30
+ execution: { mode: 'turn' },
31
+ }),
32
+ );
33
+ const resolved = resolveNpmSkillPackage(pkgRoot, { spawnSyncImpl: spawnSync });
34
+ assert.ok(fs.existsSync(path.join(resolved, 'agentsam.skill.json')));
35
+
36
+ const home = tmpHome();
37
+ const installed = installSkillFromPath(resolved, { home });
38
+ assert.equal(installed.id, 'npm-fixture');
39
+ assert.equal(installed.trigger, '/npm-fixture');
40
+ });
41
+
42
+ test('env boot-line prints source load-agent-env command', async () => {
43
+ const home = tmpHome();
44
+ const lines = [];
45
+ const result = await runEnv(['boot-line', 'cursor', 'inneranimalmedia'], {
46
+ home,
47
+ write: (t) => lines.push(t),
48
+ });
49
+ assert.equal(result.command, 'source ~/.agentsam/load-agent-env.sh');
50
+ assert.ok(lines.join('').includes('load-agent-env.sh'));
51
+ assert.ok(lines.join('').includes('agentsam env shell'));
52
+ });