@agent-native/core 0.37.0 → 0.37.1

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.
@@ -55,6 +55,22 @@ const SHARED_RESOURCE_INDEX_LIMIT = 40;
55
55
  function normalizeResourcePathForPrompt(path) {
56
56
  return path.replace(/^\/+/, "").trim();
57
57
  }
58
+ function resourceToolHint(action, extra) {
59
+ return `Use the \`resources\` tool with \`action: "${action}"\`${extra ? `, ${extra}` : ""}.`;
60
+ }
61
+ function skillDocsSlug(name) {
62
+ return `skill-${name
63
+ .trim()
64
+ .toLowerCase()
65
+ .replace(/[^a-z0-9]+/g, "-")
66
+ .replace(/^-+|-+$/g, "")}`;
67
+ }
68
+ function ensureSentence(value) {
69
+ const trimmed = value.trim();
70
+ if (!trimmed)
71
+ return "";
72
+ return /[.!?]$/.test(trimmed) ? trimmed : `${trimmed}.`;
73
+ }
58
74
  function escapeXmlAttribute(value) {
59
75
  return value.replace(/&/g, "&").replace(/"/g, """);
60
76
  }
@@ -64,7 +80,7 @@ function truncatePromptResourceContent(content, path, maxChars = SHARED_PROMPT_R
64
80
  return trimmed;
65
81
  const omitted = trimmed.length - maxChars;
66
82
  const hint = readHint ??
67
- `Use resource-read --path "${path}" with the resource's scope for the full content.`;
83
+ resourceToolHint("read", `\`path: "${path}"\` and the resource's \`scope\` for the full content`);
68
84
  return `${trimmed.slice(0, maxChars)}\n\n[Resource ${path} truncated after ${maxChars.toLocaleString()} characters; ${omitted.toLocaleString()} characters omitted. ${hint}]`;
69
85
  }
70
86
  function promptResourceBlock(input) {
@@ -214,7 +230,7 @@ async function loadResourceSkillsPromptBlock(owner) {
214
230
  seen.add(name);
215
231
  const scope = resourceScopeForOwner(resource.owner, owner);
216
232
  const description = meta.description || "(no description)";
217
- lines.push(`- \`${name}\` at resource \`${resource.path}\` (${scope}) - ${description}. Read it with \`resource-read --path "${resource.path}" --scope ${scope}\` before starting a task it applies to.`);
233
+ lines.push(`- \`${name}\` at resource \`${resource.path}\` (${scope}) - ${ensureSentence(description)} ${resourceToolHint("read", `\`path: "${resource.path}"\` and \`scope: "${scope}"\` before starting a task it applies to`)}`);
218
234
  }
219
235
  if (lines.length === 0)
220
236
  return null;
@@ -242,12 +258,12 @@ async function loadResourceIndexForPrompt(owner, scope) {
242
258
  lines.push(`- \`${resource.path}\`${summary ? ` - ${summary}` : ""}`);
243
259
  }
244
260
  if (resources.length > listed.length) {
245
- lines.push(`- ...${resources.length - listed.length} more ${scope} resources. Use \`resource-list --scope ${scope}\` to inspect them.`);
261
+ lines.push(`- ...${resources.length - listed.length} more ${scope} resources. ${resourceToolHint("list", `\`scope: "${scope}"\` to inspect them`)}`);
246
262
  }
247
263
  const label = scope === "workspace"
248
264
  ? "Workspace reference resources are inherited by every app and are available for company, brand, positioning, persona, product, or domain context."
249
265
  : "Shared app/organization reference resources are available for app-specific or team context.";
250
- return `<workspace-resources scope="${scope}">\n${label} Use \`resource-read --path <path> --scope ${scope}\` when a task may depend on them; do not assume their contents without reading the relevant file.\n\n${lines.join("\n")}\n</workspace-resources>`;
266
+ return `<workspace-resources scope="${scope}">\n${label} ${resourceToolHint("read", `\`path: <path>\` and \`scope: "${scope}"\` when a task may depend on them`)} Do not assume their contents without reading the relevant file.\n\n${lines.join("\n")}\n</workspace-resources>`;
251
267
  }
252
268
  catch {
253
269
  return null;
@@ -748,7 +764,7 @@ async function createDocsScriptEntries() {
748
764
  const mod = await import("../scripts/docs/search.js");
749
765
  return {
750
766
  "docs-search": wrapCliScript({
751
- description: "Search and read agent-native framework documentation. Use --list to see all pages, --query to search, --slug to read a specific page.",
767
+ description: "Search and read agent-native framework documentation, bundled AGENTS.md, and codebase skills. Use --list to see all pages, --query to search, --slug to read a specific page. Codebase skill pages use slugs like skill-<name>.",
752
768
  parameters: {
753
769
  type: "object",
754
770
  properties: {
@@ -1631,8 +1647,8 @@ Bring a senior engineer's judgment, arrived at through attention not premature c
1631
1647
 
1632
1648
  ### Resources
1633
1649
 
1634
- Use resource-list, resource-read, resource-effective, resource-write, resource-delete for persistent notes and context files.
1635
- Resources have three levels: workspace defaults inherited from Dispatch, shared organization/app overrides, and personal overrides. Use resource-effective before editing when you need to explain or inspect which level is active for a path.
1650
+ Use the \`resources\` tool for persistent notes and context files: \`action: "list"\`, \`"read"\`, \`"effective"\`, \`"write"\`, \`"promote"\`, or \`"delete"\`.
1651
+ Resources have three levels: workspace defaults inherited from Dispatch, shared organization/app overrides, and personal overrides. Use \`resources\` with \`action: "effective"\` before editing when you need to explain or inspect which level is active for a path.
1636
1652
  Workspace resources are user-facing by default. If you need temporary working files, write them as agent scratch (\`visibility: "agent_scratch"\`); scratch is hidden from the Workspace view by default and expires. Use \`visibility: "workspace"\` only when the user explicitly asked to save/manage that file, or for durable AGENTS.md, LEARNINGS.md, memory, skills, jobs, or custom agents.
1637
1653
 
1638
1654
  ### Navigation Rule
@@ -1726,7 +1742,7 @@ You can create recurring jobs that run on a cron schedule. Jobs are resource fil
1726
1742
  - \`manage-jobs\` (action: "create") — Create a new recurring job with a cron schedule and instructions
1727
1743
  - \`manage-jobs\` (action: "list") — List all recurring jobs and their status
1728
1744
  - \`manage-jobs\` (action: "update") — Update a job's schedule, instructions, or toggle enabled/disabled
1729
- - Delete a job with \`resource-delete --path jobs/<name>.md\`
1745
+ - Delete a job with the \`resources\` tool: \`action: "delete"\`, \`path: "jobs/<name>.md"\`
1730
1746
 
1731
1747
  Convert natural language to 5-field cron format:
1732
1748
  - "every morning" / "daily at 9am" → \`0 9 * * *\`
@@ -1789,7 +1805,7 @@ Your memory index (\`memory/MEMORY.md\`) is loaded at the start of every convers
1789
1805
  **Tools:**
1790
1806
  - \`save-memory\` — Create or update a memory (name, type, description, content)
1791
1807
  - \`delete-memory\` — Remove a memory and its index entry
1792
- - \`resource-read --path memory/<name>.md\` — Read the full content of a specific memory
1808
+ - \`resources\` with \`action: "read"\` and \`path: "memory/<name>.md"\` — Read the full content of a specific memory
1793
1809
 
1794
1810
  **Memory types:** user, feedback, project, reference
1795
1811
 
@@ -1879,7 +1895,7 @@ Gather context efficiently: when you need several independent read-only lookups
1879
1895
  ### Resources
1880
1896
 
1881
1897
  You have access to a Resources system for persistent notes and context files.
1882
- Use resource-list, resource-read, resource-effective, resource-write, resource-delete to manage resources.
1898
+ Use the \`resources\` tool to manage resources: \`action: "list"\`, \`"read"\`, \`"effective"\`, \`"write"\`, \`"promote"\`, or \`"delete"\`.
1883
1899
  Resources can be workspace defaults inherited from Dispatch, shared organization/app overrides, or personal overrides. By default, resources are personal. Workspace-scope resources are read-only from app agents; create shared or personal resources to override or narrow them.
1884
1900
 
1885
1901
  When the user gives instructions that should apply to all users/sessions, update the shared "AGENTS.md" resource.
@@ -1976,7 +1992,7 @@ In Act mode, when the user asks you to change the UI, modify code, add a feature
1976
1992
 
1977
1993
  When \`builderEnabled\` is false, Builder Cloud Agents are not self-serve from Builder org settings. Do NOT tell the user to go to Builder org settings, beta features, "Cloud Agents", or "AI Agents" to enable them. The only allowed guidance is the card's waitlist/local-dev fallback: join the waitlist when the card offers it, use Agent Native Desktop, or edit from a local clone.
1978
1994
 
1979
- **Act-mode handoff — do exactly this:** The moment a code change is requested, call \`connect-builder\` with the user's verbatim request, then write one sentence framing the next click. That is the whole job: Builder plans and edits in its own sandbox, so the planning, file-mapping, and code all happen there, not here. Keep your turn to the call plus that one sentence — skip code exploration, implementation plans, \`resource-write\` of specs, and sub-agents (they have no code-editing tools either). You don't need filesystem access to recommend Builder, so don't reach for it or list tools you lack.
1995
+ **Act-mode handoff — do exactly this:** The moment a code change is requested, call \`connect-builder\` with the user's verbatim request, then write one sentence framing the next click. That is the whole job: Builder plans and edits in its own sandbox, so the planning, file-mapping, and code all happen there, not here. Keep your turn to the call plus that one sentence — skip code exploration, implementation plans, \`resources\` writes of specs, and sub-agents (they have no code-editing tools either). You don't need filesystem access to recommend Builder, so don't reach for it or list tools you lack.
1980
1996
 
1981
1997
  Builder Cloud Agents are the recommended path when available because they are one-click and run in the cloud. Local dev and the desktop app are the fallback when cloud agent access is waitlisted or unavailable.
1982
1998
  ${FRAMEWORK_CORE}`;
@@ -2120,10 +2136,13 @@ export async function loadResourcesForPrompt(owner, compact = false, selfAppId)
2120
2136
  sections.push(skillsBlock);
2121
2137
  }
2122
2138
  else if (Object.keys(bundle.skills).length > 0) {
2123
- const names = Object.values(bundle.skills)
2124
- .map((s) => s.meta.name)
2125
- .join(", ");
2126
- sections.push(`<skills-summary>\nSkills available in .agents/skills/: ${names}. Use \`docs-search\` to read a skill before starting a task it applies to.\n</skills-summary>`);
2139
+ const lines = Object.values(bundle.skills).map((s) => {
2140
+ const description = s.meta.description?.trim()
2141
+ ? ` - ${ensureSentence(s.meta.description)}`
2142
+ : "";
2143
+ return `- \`${s.meta.name}\`${description} Read with \`docs-search --slug "${skillDocsSlug(s.meta.name)}"\` before starting a task it applies to.`;
2144
+ });
2145
+ sections.push(`<skills-summary>\nCodebase skills bundled from \`.agents/skills/\` (or legacy \`.agent/skills/\`) are available as docs-search pages. Do not use MCP resource reads for these skills.\n\n${lines.join("\n")}\n</skills-summary>`);
2127
2146
  }
2128
2147
  }
2129
2148
  catch { }
@@ -2152,9 +2171,9 @@ export async function loadResourcesForPrompt(owner, compact = false, selfAppId)
2152
2171
  sections.push(resourceSkillsBlock);
2153
2172
  if (compact) {
2154
2173
  // In compact mode, skip learnings and memory in the prompt.
2155
- // The agent can access them via resource-read when needed.
2174
+ // The agent can access them via the resources tool when needed.
2156
2175
  // Add a brief pointer so the agent knows they exist.
2157
- sections.push(`<context-note>Shared learnings (LEARNINGS.md) and your personal memory (memory/MEMORY.md) are available via \`resource-read\`. Check them when making decisions that might benefit from prior context.</context-note>`);
2176
+ sections.push(`<context-note>Shared learnings (LEARNINGS.md) and your personal memory (memory/MEMORY.md) are available via the \`resources\` tool with \`action: "read"\`. Check them when making decisions that might benefit from prior context.</context-note>`);
2158
2177
  }
2159
2178
  else {
2160
2179
  // LEARNINGS.md from SQL (template-level instructions are in AGENTS.md above).
@@ -4278,57 +4297,74 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
4278
4297
  }
4279
4298
  const skills = [];
4280
4299
  const seenNames = new Set();
4281
- // In dev mode, scan .agents/skills/ directory
4300
+ // In dev mode, scan .agents/skills/ plus legacy .agent/skills/.
4282
4301
  if (currentDevMode) {
4283
4302
  try {
4284
4303
  const _fs = await lazyFs();
4285
- const skillsDir = nodePath.join(process.cwd(), ".agents", "skills");
4286
- const entries = _fs.readdirSync(skillsDir, {
4287
- withFileTypes: true,
4288
- });
4289
- for (const entry of entries) {
4290
- // Support both flat .md files and subdirectory-based skills (dir/SKILL.md)
4291
- let skillFilePath;
4292
- let skillRelPath;
4293
- if (entry.isDirectory()) {
4294
- // Subdirectory layout: .agents/skills/<name>/SKILL.md
4295
- const candidate = nodePath.join(skillsDir, entry.name, "SKILL.md");
4296
- if (!_fs.existsSync(candidate))
4297
- continue;
4298
- skillFilePath = candidate;
4299
- skillRelPath = `.agents/skills/${entry.name}/SKILL.md`;
4300
- }
4301
- else if (entry.isFile() && entry.name.endsWith(".md")) {
4302
- // Flat layout: .agents/skills/<name>.md
4303
- skillFilePath = nodePath.join(skillsDir, entry.name);
4304
- skillRelPath = `.agents/skills/${entry.name}`;
4304
+ const skillRoots = [
4305
+ {
4306
+ dir: nodePath.join(process.cwd(), ".agents", "skills"),
4307
+ display: ".agents/skills",
4308
+ },
4309
+ {
4310
+ dir: nodePath.join(process.cwd(), ".agent", "skills"),
4311
+ display: ".agent/skills",
4312
+ },
4313
+ ];
4314
+ for (const root of skillRoots) {
4315
+ let entries;
4316
+ try {
4317
+ entries = _fs.readdirSync(root.dir, {
4318
+ withFileTypes: true,
4319
+ });
4305
4320
  }
4306
- else {
4321
+ catch {
4307
4322
  continue;
4308
4323
  }
4309
- try {
4310
- const content = _fs.readFileSync(skillFilePath, "utf-8");
4311
- const fm = parseSkillFrontmatter(content);
4312
- if (fm.userInvocable === false)
4324
+ for (const entry of entries) {
4325
+ // Support both flat .md files and subdirectory-based skills (dir/SKILL.md)
4326
+ let skillFilePath;
4327
+ let skillRelPath;
4328
+ if (entry.isDirectory()) {
4329
+ // Subdirectory layout: <skills-root>/<name>/SKILL.md
4330
+ const candidate = nodePath.join(root.dir, entry.name, "SKILL.md");
4331
+ if (!_fs.existsSync(candidate))
4332
+ continue;
4333
+ skillFilePath = candidate;
4334
+ skillRelPath = `${root.display}/${entry.name}/SKILL.md`;
4335
+ }
4336
+ else if (entry.isFile() && entry.name.endsWith(".md")) {
4337
+ // Flat layout: <skills-root>/<name>.md
4338
+ skillFilePath = nodePath.join(root.dir, entry.name);
4339
+ skillRelPath = `${root.display}/${entry.name}`;
4340
+ }
4341
+ else {
4313
4342
  continue;
4314
- const skillName = fm.name || entry.name.replace(/\.md$/, "");
4315
- if (!seenNames.has(skillName)) {
4316
- seenNames.add(skillName);
4317
- skills.push({
4318
- name: skillName,
4319
- description: fm.description,
4320
- path: skillRelPath,
4321
- source: "codebase",
4322
- });
4323
4343
  }
4324
- }
4325
- catch {
4326
- // Could not read individual skill file — skip
4344
+ try {
4345
+ const content = _fs.readFileSync(skillFilePath, "utf-8");
4346
+ const fm = parseSkillFrontmatter(content);
4347
+ if (fm.userInvocable === false)
4348
+ continue;
4349
+ const skillName = fm.name || entry.name.replace(/\.md$/, "");
4350
+ if (!seenNames.has(skillName)) {
4351
+ seenNames.add(skillName);
4352
+ skills.push({
4353
+ name: skillName,
4354
+ description: fm.description,
4355
+ path: skillRelPath,
4356
+ source: "codebase",
4357
+ });
4358
+ }
4359
+ }
4360
+ catch {
4361
+ // Could not read individual skill file — skip
4362
+ }
4327
4363
  }
4328
4364
  }
4329
4365
  }
4330
4366
  catch {
4331
- // .agents/skills/ directory doesn't exist or not readable — skip
4367
+ // Skill directories don't exist or are not readable — skip.
4332
4368
  }
4333
4369
  }
4334
4370
  // Query accessible resources with skills/ prefix. Personal skills