@agentxm/extension-content 0.38.0 → 0.39.0

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 (34) hide show
  1. package/dist/src/content/frontmatter.js +9 -22
  2. package/dist/src/content/skill-content.d.ts +14 -0
  3. package/dist/src/content/skill-content.js +24 -0
  4. package/dist/src/content/subagent-content.d.ts +70 -46
  5. package/dist/src/content/subagent-content.js +273 -85
  6. package/dist/src/index.d.ts +4 -4
  7. package/dist/src/index.js +4 -4
  8. package/dist/src/knowledge/package-inspection.d.ts +7 -7
  9. package/dist/src/lint/catalog/hook/entrypoint-exists.js +18 -15
  10. package/dist/src/lint/catalog/hook/envelope.d.ts +14 -1
  11. package/dist/src/lint/catalog/hook/envelope.js +25 -2
  12. package/dist/src/lint/catalog/hook/native-bindings-supported.d.ts +4 -0
  13. package/dist/src/lint/catalog/hook/native-bindings-supported.js +39 -0
  14. package/dist/src/lint/catalog/hook.js +2 -4
  15. package/dist/src/lint/catalog/skill-accessor/contexts.d.ts +1 -0
  16. package/dist/src/lint/catalog/skill-accessor/contexts.js +3 -0
  17. package/dist/src/lint/catalog/skill.d.ts +1 -1
  18. package/dist/src/lint/catalog/skill.js +20 -8
  19. package/dist/src/lint/catalog/subagent/envelope.js +1 -1
  20. package/dist/src/lint/catalog-metadata.js +3 -9
  21. package/dist/src/lint/context.d.ts +2 -0
  22. package/dist/src/packaging/archive-guardrails.d.ts +1 -0
  23. package/dist/src/packaging/archive-guardrails.js +205 -11
  24. package/dist/src/packaging/filtered-package-validation.js +24 -31
  25. package/dist/src/packaging/input-normalization.d.ts +0 -1
  26. package/dist/src/packaging/input-normalization.js +1 -77
  27. package/dist/src/packaging/manifest-file.js +1 -1
  28. package/dist/src/packaging/manifest-policy.d.ts +239 -109
  29. package/dist/src/packaging/manifest-policy.js +1 -1
  30. package/package.json +4 -2
  31. package/dist/src/lint/catalog/hook/decision-portability.d.ts +0 -4
  32. package/dist/src/lint/catalog/hook/decision-portability.js +0 -40
  33. package/dist/src/lint/catalog/hook/matcher-raw-portability.d.ts +0 -4
  34. package/dist/src/lint/catalog/hook/matcher-raw-portability.js +0 -51
@@ -13,7 +13,6 @@ import YAML, { YAMLParseError } from "yaml";
13
13
  /** Bounded, document-relative failure from parsing a YAML frontmatter block. */
14
14
  export class FrontmatterParseFailure extends Data.TaggedError("FrontmatterParseFailure") {
15
15
  }
16
- const FRONTMATTER_DELIMITER = "---";
17
16
  export const FRONTMATTER_PARSE_FALLBACK_REASON = "YAML frontmatter could not be parsed";
18
17
  const FRONTMATTER_PARSE_REASON_MAX_CODE_POINTS = 256;
19
18
  const boundedReason = (reason) => {
@@ -52,27 +51,15 @@ export const normalizeFrontmatterParseFailure = (error, documentLineOffset) => {
52
51
  * Returns [endOfOpening, startOfClosing] indices, or undefined if no frontmatter.
53
52
  */
54
53
  const findFrontmatterBoundaries = (content) => {
55
- if (!content.startsWith(FRONTMATTER_DELIMITER)) {
54
+ const opening = /^---\r?\n/u.exec(content);
55
+ if (opening === null)
56
56
  return undefined;
57
- }
58
- const afterOpening = content.indexOf("\n");
59
- if (afterOpening === -1) {
60
- return undefined;
61
- }
62
- const closingIndex = content.indexOf(`\n${FRONTMATTER_DELIMITER}`, afterOpening);
63
- if (closingIndex === -1) {
57
+ const yamlStart = opening[0].length;
58
+ const closing = /^---(?:\r?\n|$)/mu.exec(content.slice(yamlStart));
59
+ if (closing === null)
64
60
  return undefined;
65
- }
66
- const closingEnd = closingIndex + 1 + FRONTMATTER_DELIMITER.length;
67
- const bodyStart = content.startsWith("\r\n", closingEnd)
68
- ? closingEnd + 2
69
- : content[closingEnd] === "\n"
70
- ? closingEnd + 1
71
- : closingEnd;
72
- return {
73
- yamlStart: afterOpening + 1,
74
- bodyStart,
75
- };
61
+ const yamlEnd = yamlStart + closing.index;
62
+ return { yamlStart, yamlEnd, bodyStart: yamlEnd + closing[0].length };
76
63
  };
77
64
  /**
78
65
  * Parse frontmatter from content synchronously.
@@ -86,10 +73,10 @@ export const parseFrontmatterSync = (content) => {
86
73
  if (boundaries === undefined) {
87
74
  return { frontmatter: undefined, body: content };
88
75
  }
89
- const yamlContent = content.slice(boundaries.yamlStart, boundaries.bodyStart).split("---")[0];
76
+ const yamlContent = content.slice(boundaries.yamlStart, boundaries.yamlEnd);
90
77
  const frontmatter = (() => {
91
78
  try {
92
- return YAML.parse(yamlContent ?? "");
79
+ return YAML.parse(yamlContent);
93
80
  }
94
81
  catch (error) {
95
82
  const documentLineOffset = content.slice(0, boundaries.yamlStart).split("\n").length - 1;
@@ -30,6 +30,20 @@ export type SkillFrontmatterValidation = {
30
30
  readonly valid: false;
31
31
  readonly errors: ReadonlyArray<string>;
32
32
  };
33
+ /** Best-effort display metadata from an externally distributed skill. */
34
+ export interface ExtractedSkillMetadata {
35
+ readonly name?: string;
36
+ readonly description?: string;
37
+ readonly metadata?: unknown;
38
+ readonly frontmatter?: Readonly<Record<string, unknown>>;
39
+ }
40
+ /**
41
+ * Read external metadata without applying AXM's authoring contract. The source
42
+ * remains authoritative: unknown fields, display names and metadata values are
43
+ * retained as declared. Missing or malformed metadata does not make an explicit
44
+ * SKILL.md path unavailable to acquisition.
45
+ */
46
+ export declare const extractSkillMetadata: (content: string) => ExtractedSkillMetadata;
33
47
  /** Validate parsed YAML against the pinned Agent Skills metadata contract. */
34
48
  export declare const validateSkillFrontmatter: (input: unknown, expectedDirectoryName?: string) => SkillFrontmatterValidation;
35
49
  /**
@@ -49,6 +49,30 @@ export const SkillFrontmatterSchema = Schema.Struct({
49
49
  });
50
50
  const isRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
51
51
  const allowedFrontmatterFields = new Set(AGENT_SKILLS_FRONTMATTER_FIELDS);
52
+ /**
53
+ * Read external metadata without applying AXM's authoring contract. The source
54
+ * remains authoritative: unknown fields, display names and metadata values are
55
+ * retained as declared. Missing or malformed metadata does not make an explicit
56
+ * SKILL.md path unavailable to acquisition.
57
+ */
58
+ export const extractSkillMetadata = (content) => {
59
+ try {
60
+ const { frontmatter } = parseFrontmatterSync(content.replace(/^\uFEFF/, ""));
61
+ if (!isRecord(frontmatter))
62
+ return {};
63
+ const name = frontmatter["name"];
64
+ const description = frontmatter["description"];
65
+ return {
66
+ ...(typeof name === "string" && name.trim().length > 0 ? { name } : {}),
67
+ ...(typeof description === "string" && description.trim().length > 0 ? { description } : {}),
68
+ ...(frontmatter["metadata"] === undefined ? {} : { metadata: frontmatter["metadata"] }),
69
+ frontmatter,
70
+ };
71
+ }
72
+ catch {
73
+ return {};
74
+ }
75
+ };
52
76
  /** Validate parsed YAML against the pinned Agent Skills metadata contract. */
53
77
  export const validateSkillFrontmatter = (input, expectedDirectoryName) => {
54
78
  if (!isRecord(input)) {
@@ -1,58 +1,82 @@
1
- /**
2
- * Subagent content file module for subagent content parsing.
3
- *
4
- * The content file's YAML frontmatter is largely free-form: only `name` is
5
- * required and validated. `agentOverrides`, when present, is recognized for
6
- * use in per-agent rendering. Any other frontmatter keys flow through
7
- * verbatim to the agent-native file produced by the renderer.
8
- *
9
- * @experimental This API is unstable and may change without notice.
10
- */
11
1
  import * as Effect from "effect/Effect";
12
- import * as Option from "effect/Option";
2
+ import * as FileSystem from "effect/FileSystem";
3
+ import * as Path from "effect/Path";
4
+ import { type SubagentManifest } from "@agentxm/extension-model/unstable/subagents/manifest-schema";
13
5
  declare const SubagentContentError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
14
6
  readonly _tag: "SubagentContentError";
15
7
  } & Readonly<A>;
16
- /**
17
- * Typed failure produced while parsing a subagent content file. Application
18
- * layers translate this into their own error vocabulary.
19
- *
20
- * @experimental This API is unstable and may change without notice.
21
- */
22
8
  export declare class SubagentContentError extends SubagentContentError_base<{
23
- readonly reason: "frontmatter-parse" | "missing-frontmatter" | "invalid-frontmatter" | "name-mismatch";
9
+ readonly reason: "manifest-invalid" | "reference-invalid" | "content-invalid" | "native-invalid";
24
10
  readonly detail: string;
25
- readonly suggestion?: string;
11
+ readonly source?: string;
26
12
  readonly cause?: unknown;
27
13
  }> {
28
14
  }
29
- /**
30
- * Map of agent-id → merge patch applied during per-agent rendering.
31
- *
32
- * @experimental This API is unstable and may change without notice.
33
- */
34
- export type SubagentAgentOverrides = Readonly<Record<string, Readonly<Record<string, unknown>>>>;
35
- /**
36
- * Result of parsing a subagent content file.
37
- *
38
- * @experimental This API is unstable and may change without notice.
39
- */
40
- export interface SubagentContentResult {
41
- /** The full frontmatter map, opaque except for `name`. Empty when no frontmatter. */
42
- readonly frontmatter: Option.Option<Readonly<Record<string, unknown>>>;
43
- /** The `agentOverrides` map keyed by agent id, when present and structurally valid. */
44
- readonly agentOverrides: Option.Option<SubagentAgentOverrides>;
45
- /** Content body after the frontmatter block, or full content if no frontmatter. */
46
- readonly body: string;
15
+ export interface NativeSubagentContent {
16
+ readonly format: "markdown" | "toml" | "json";
17
+ /** Exact author-supplied native source; AXM never merges a core into it. */
18
+ readonly content: string;
19
+ readonly configuration: Readonly<Record<string, unknown>>;
20
+ readonly name: string;
21
+ readonly description?: string;
22
+ readonly instructions: string;
47
23
  }
48
- /**
49
- * Parse a subagent content file into validated frontmatter and body.
50
- *
51
- * Validates only that frontmatter is present and that its `name` matches
52
- * the expected name. All other keys are preserved verbatim.
53
- *
54
- * @experimental This API is unstable and may change without notice.
55
- */
56
- export declare const parseSubagentMd: (content: string, expectedName: string) => Effect.Effect<SubagentContentResult, SubagentContentError>;
24
+ /** Known native syntax, independent of AXM's scope, ownership, or writer support. */
25
+ export declare const nativeSubagentFormat: (agentId: string) => NativeSubagentContent["format"] | undefined;
26
+ export type ResolvedSubagentImplementation = {
27
+ readonly kind: "customized";
28
+ readonly configuration: Readonly<Record<string, unknown>>;
29
+ readonly instructions?: {
30
+ readonly mode: "append" | "replace";
31
+ readonly source: string;
32
+ readonly content: string;
33
+ };
34
+ } | {
35
+ readonly kind: "native";
36
+ readonly source: string;
37
+ readonly native: NativeSubagentContent;
38
+ };
39
+ export interface DecodedSubagentPackage {
40
+ readonly manifest: SubagentManifest;
41
+ readonly core?: {
42
+ readonly name: string;
43
+ readonly description: string;
44
+ readonly instructions: string;
45
+ readonly source: string;
46
+ };
47
+ readonly implementations: Readonly<Record<string, ResolvedSubagentImplementation>>;
48
+ }
49
+ export type SelectedSubagentImplementation = {
50
+ readonly kind: "core" | "customized";
51
+ readonly name: string;
52
+ readonly description: string;
53
+ readonly instructions: string;
54
+ readonly configuration: Readonly<Record<string, unknown>>;
55
+ readonly sourceDependencies: ReadonlyArray<string>;
56
+ } | {
57
+ readonly kind: "native";
58
+ readonly source: string;
59
+ readonly native: NativeSubagentContent;
60
+ readonly sourceDependencies: ReadonlyArray<string>;
61
+ } | {
62
+ readonly kind: "unsupported";
63
+ readonly reason: string;
64
+ readonly sourceDependencies: ReadonlyArray<string>;
65
+ };
66
+ /** Parse a complete native definition without discarding unknown native fields. */
67
+ export declare const parseNativeSubagent: (args: {
68
+ readonly agentId: string;
69
+ readonly source: string;
70
+ readonly content: string;
71
+ }) => Effect.Effect<NativeSubagentContent, SubagentContentError>;
72
+ /** Validate and resolve every declared compile-time input using a package-contained reader. */
73
+ export declare const readSubagentPackage: <E, R>(args: {
74
+ readonly manifest: unknown;
75
+ readonly readFile: (relativePath: string) => Effect.Effect<string, E, R>;
76
+ }) => Effect.Effect<DecodedSubagentPackage, SubagentContentError | E, R>;
77
+ /** Read regular source files only after resolving them inside the owning package. */
78
+ export declare const loadSubagentPackage: (packageRoot: string) => Effect.Effect<DecodedSubagentPackage, SubagentContentError, FileSystem.FileSystem | Path.Path>;
79
+ /** Select one explicit implementation; a complete native definition bypasses the core. */
80
+ export declare const selectSubagentImplementation: (pkg: DecodedSubagentPackage, agentId: string) => SelectedSubagentImplementation;
57
81
  export {};
58
82
  //# sourceMappingURL=subagent-content.d.ts.map
@@ -1,101 +1,289 @@
1
- /**
2
- * Subagent content file module for subagent content parsing.
3
- *
4
- * The content file's YAML frontmatter is largely free-form: only `name` is
5
- * required and validated. `agentOverrides`, when present, is recognized for
6
- * use in per-agent rendering. Any other frontmatter keys flow through
7
- * verbatim to the agent-native file produced by the renderer.
8
- *
9
- * @experimental This API is unstable and may change without notice.
10
- */
1
+ /** Native definition parsing and package selection shared by every subagent workflow. */
2
+ // @effect-diagnostics anyUnknownInErrorContext:off — package readers preserve caller-owned read errors
11
3
  import * as Data from "effect/Data";
12
4
  import * as Effect from "effect/Effect";
13
- import * as Option from "effect/Option";
5
+ import * as FileSystem from "effect/FileSystem";
6
+ import * as Path from "effect/Path";
14
7
  import * as Schema from "effect/Schema";
8
+ import { parse as parseToml } from "smol-toml";
9
+ import { MANIFEST_FILENAME, SubagentManifestSchema, SubagentNativeNameSchema, } from "@agentxm/extension-model/unstable/subagents/manifest-schema";
15
10
  import { parseFrontmatterEffect } from "./frontmatter.js";
16
- /**
17
- * Typed failure produced while parsing a subagent content file. Application
18
- * layers translate this into their own error vocabulary.
19
- *
20
- * @experimental This API is unstable and may change without notice.
21
- */
22
11
  export class SubagentContentError extends Data.TaggedError("SubagentContentError") {
23
12
  }
24
- /**
25
- * Schema for the only required frontmatter field — `name`.
26
- *
27
- * Used to validate that `name` is present and a string. The full frontmatter
28
- * map is preserved verbatim and surfaced as `frontmatter`.
29
- */
30
- const NameOnlySchema = Schema.Struct({
31
- name: Schema.String,
32
- }).annotate({
33
- identifier: "SubagentFrontmatterName",
34
- description: "Required `name` field for subagent frontmatter.",
35
- });
36
- const isPlainObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
37
- const extractAgentOverrides = (fm) => {
38
- const raw = fm["agentOverrides"];
39
- if (!isPlainObject(raw))
40
- return Option.none();
41
- const out = {};
42
- for (const [agentId, patch] of Object.entries(raw)) {
43
- if (isPlainObject(patch))
44
- out[agentId] = patch;
45
- }
46
- return Option.some(out);
13
+ const nativeFormats = {
14
+ antigravity: "markdown",
15
+ "antigravity-cli": "markdown",
16
+ "claude-code": "markdown",
17
+ "github-copilot-cli": "markdown",
18
+ cursor: "markdown",
19
+ "gemini-cli": "markdown",
20
+ opencode: "markdown",
21
+ augment: "markdown",
22
+ junie: "markdown",
23
+ kilo: "markdown",
24
+ "kimi-cli": "markdown",
25
+ codebuddy: "markdown",
26
+ "mimo-code": "markdown",
27
+ kode: "markdown",
28
+ "codearts-agent": "markdown",
29
+ "iflow-cli": "markdown",
30
+ rovodev: "markdown",
31
+ "qwen-code": "markdown",
32
+ qoder: "markdown",
33
+ "qoder-cn": "markdown",
34
+ "grok-cli": "markdown",
35
+ "command-code": "markdown",
36
+ mux: "markdown",
37
+ devin: "markdown",
38
+ codex: "toml",
39
+ "mistral-vibe": "toml",
40
+ "kiro-cli": "json",
41
+ roo: "json",
47
42
  };
48
- /**
49
- * Parse a subagent content file into validated frontmatter and body.
50
- *
51
- * Validates only that frontmatter is present and that its `name` matches
52
- * the expected name. All other keys are preserved verbatim.
53
- *
54
- * @experimental This API is unstable and may change without notice.
55
- */
56
- export const parseSubagentMd = (content, expectedName) => Effect.gen(function* () {
57
- const parsed = yield* parseFrontmatterEffect(content).pipe(Effect.mapError((cause) => new SubagentContentError({
58
- reason: "frontmatter-parse",
59
- detail: "YAML frontmatter could not be parsed",
60
- suggestion: "Ensure the frontmatter block contains valid YAML between --- delimiters.",
61
- cause,
62
- })));
63
- if (parsed.frontmatter === undefined) {
64
- return yield* new SubagentContentError({
65
- reason: "missing-frontmatter",
66
- detail: `Missing subagent frontmatter for "${expectedName}"`,
67
- suggestion: `Add YAML frontmatter with name: ${expectedName}.`,
43
+ /** Known native syntax, independent of AXM's scope, ownership, or writer support. */
44
+ export const nativeSubagentFormat = (agentId) => nativeFormats[agentId];
45
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
46
+ /** Reject dangerous object keys, including nested keys, before downstream merging. */
47
+ const hasUnsafeKeys = (value, seen = new Set()) => {
48
+ if (typeof value !== "object" || value === null)
49
+ return false;
50
+ if (seen.has(value))
51
+ return true;
52
+ seen.add(value);
53
+ const unsafe = Object.entries(value).some(([key, child]) => ["__proto__", "prototype", "constructor", "agentOverrides"].includes(key) ||
54
+ hasUnsafeKeys(child, seen));
55
+ seen.delete(value);
56
+ return unsafe;
57
+ };
58
+ /** Parse a complete native definition without discarding unknown native fields. */
59
+ export const parseNativeSubagent = (args) => Effect.gen(function* () {
60
+ const invalid = (detail) => new SubagentContentError({
61
+ reason: "native-invalid",
62
+ source: args.source,
63
+ detail,
64
+ });
65
+ const extension = args.source.split(".").at(-1)?.toLowerCase();
66
+ const format = extension === "md"
67
+ ? "markdown"
68
+ : extension === "toml"
69
+ ? "toml"
70
+ : extension === "json"
71
+ ? "json"
72
+ : undefined;
73
+ if (format === undefined) {
74
+ return yield* invalid(`Native source "${args.source}" must be Markdown, TOML, or JSON.`);
75
+ }
76
+ const expectedFormat = nativeSubagentFormat(args.agentId);
77
+ if (expectedFormat !== undefined && expectedFormat !== format) {
78
+ return yield* invalid(`Native source "${args.source}" must use ${expectedFormat} for ${args.agentId}.`);
79
+ }
80
+ let configuration;
81
+ let instructions;
82
+ if (format === "markdown") {
83
+ const parsed = yield* parseFrontmatterEffect(args.content).pipe(Effect.mapError(() => invalid(`Native source "${args.source}" has invalid YAML frontmatter.`)));
84
+ if (!isRecord(parsed.frontmatter)) {
85
+ return yield* invalid(`Native source "${args.source}" requires an object frontmatter block.`);
86
+ }
87
+ configuration = parsed.frontmatter;
88
+ instructions = parsed.body;
89
+ }
90
+ else {
91
+ const parsed = yield* Effect.try({
92
+ try: () => format === "toml" ? parseToml(args.content) : JSON.parse(args.content),
93
+ catch: () => invalid(`Native source "${args.source}" is not valid ${format.toUpperCase()}.`),
68
94
  });
95
+ if (!isRecord(parsed))
96
+ return yield* invalid(`Native source "${args.source}" must be an object.`);
97
+ configuration = parsed;
98
+ const instructionKey = format === "toml"
99
+ ? args.agentId === "codex"
100
+ ? "developer_instructions"
101
+ : "system_prompt"
102
+ : "prompt";
103
+ const value = parsed[instructionKey];
104
+ if (value !== undefined && typeof value !== "string") {
105
+ return yield* invalid(`Native source "${args.source}" requires a string ${instructionKey}.`);
106
+ }
107
+ instructions = typeof value === "string" ? value : "";
108
+ }
109
+ if (hasUnsafeKeys(configuration)) {
110
+ return yield* invalid(`Native source "${args.source}" contains unsafe or obsolete object keys.`);
111
+ }
112
+ if (args.agentId === "codex" || args.agentId === "claude-code") {
113
+ const required = args.agentId === "codex"
114
+ ? ["name", "description", "developer_instructions"]
115
+ : ["name", "description"];
116
+ for (const key of required) {
117
+ const value = configuration[key];
118
+ if (typeof value !== "string" || value.trim().length === 0) {
119
+ return yield* invalid(`Native source "${args.source}" requires nonempty ${key} for ${args.agentId}.`);
120
+ }
121
+ }
69
122
  }
70
- yield* Effect.try({
71
- try: () => Schema.decodeUnknownSync(NameOnlySchema)(parsed.frontmatter),
72
- catch: (error) => new SubagentContentError({
73
- reason: "invalid-frontmatter",
74
- detail: "Invalid subagent frontmatter",
75
- suggestion: "Frontmatter must include a string `name` field.",
76
- cause: error,
123
+ if (args.agentId === "mistral-vibe" && configuration["agent_type"] !== "subagent") {
124
+ return yield* invalid(`Native source "${args.source}" requires agent_type = "subagent".`);
125
+ }
126
+ const stem = args.source
127
+ .split("/")
128
+ .at(-1)
129
+ ?.replace(/\.[^.]+$/, "");
130
+ const nativeName = args.agentId === "opencode" || args.agentId === "mistral-vibe"
131
+ ? stem
132
+ : (configuration["name"] ?? stem);
133
+ const name = yield* Schema.decodeUnknownEffect(SubagentNativeNameSchema)(nativeName).pipe(Effect.mapError(() => invalid(`Native source "${args.source}" requires a safe native name.`)));
134
+ const description = configuration["description"];
135
+ if (description !== undefined && typeof description !== "string") {
136
+ return yield* invalid(`Native source "${args.source}" requires a string description.`);
137
+ }
138
+ return {
139
+ format,
140
+ content: args.content,
141
+ configuration,
142
+ name,
143
+ instructions,
144
+ ...(description === undefined ? {} : { description }),
145
+ };
146
+ });
147
+ /** Validate and resolve every declared compile-time input using a package-contained reader. */
148
+ export const readSubagentPackage = (args) => Effect.gen(function* () {
149
+ const manifest = yield* Schema.decodeUnknownEffect(SubagentManifestSchema, {
150
+ onExcessProperty: "error",
151
+ })(args.manifest).pipe(Effect.mapError((cause) => new SubagentContentError({
152
+ reason: "manifest-invalid",
153
+ detail: "Invalid subagent package manifest.",
154
+ cause,
155
+ })));
156
+ const core = manifest.core === undefined
157
+ ? undefined
158
+ : {
159
+ name: manifest.core.name ?? manifest.name,
160
+ description: manifest.description ?? "",
161
+ instructions: yield* args.readFile(manifest.core.instructions),
162
+ source: manifest.core.instructions,
163
+ };
164
+ const entries = yield* Effect.forEach(Object.entries(manifest.implementations ?? {}), ([agentId, implementation]) => Effect.gen(function* () {
165
+ let resolved;
166
+ if (implementation.kind === "native") {
167
+ const content = yield* args.readFile(implementation.source);
168
+ const native = yield* parseNativeSubagent({
169
+ agentId,
170
+ source: implementation.source,
171
+ content,
172
+ });
173
+ resolved = { kind: "native", source: implementation.source, native };
174
+ }
175
+ else {
176
+ if (hasUnsafeKeys(implementation.configuration)) {
177
+ return yield* new SubagentContentError({
178
+ reason: "manifest-invalid",
179
+ detail: `Implementation "${agentId}" contains unsafe configuration keys.`,
180
+ });
181
+ }
182
+ const instructions = implementation.instructions === undefined
183
+ ? undefined
184
+ : {
185
+ ...implementation.instructions,
186
+ content: yield* args.readFile(implementation.instructions.source),
187
+ };
188
+ resolved = {
189
+ kind: "customized",
190
+ configuration: implementation.configuration ?? {},
191
+ ...(instructions === undefined ? {} : { instructions }),
192
+ };
193
+ }
194
+ return [agentId, resolved];
195
+ }));
196
+ return {
197
+ manifest,
198
+ ...(core === undefined ? {} : { core }),
199
+ implementations: Object.fromEntries(entries),
200
+ };
201
+ });
202
+ /** Read regular source files only after resolving them inside the owning package. */
203
+ export const loadSubagentPackage = (packageRoot) => Effect.gen(function* () {
204
+ const fs = yield* FileSystem.FileSystem;
205
+ const path = yield* Path.Path;
206
+ const realRoot = yield* fs.realPath(packageRoot).pipe(Effect.mapError((cause) => new SubagentContentError({
207
+ reason: "reference-invalid",
208
+ detail: "Subagent package root could not be resolved.",
209
+ cause,
210
+ })));
211
+ const readFile = (source) => Effect.gen(function* () {
212
+ const sourcePath = path.join(realRoot, source);
213
+ const realSource = yield* fs.realPath(sourcePath);
214
+ const relative = path.relative(realRoot, realSource);
215
+ if (relative === "" ||
216
+ relative === ".." ||
217
+ relative.startsWith(`..${path.sep}`) ||
218
+ path.isAbsolute(relative)) {
219
+ return yield* new SubagentContentError({
220
+ reason: "reference-invalid",
221
+ source,
222
+ detail: `Source "${source}" escapes its package.`,
223
+ });
224
+ }
225
+ const info = yield* fs.stat(realSource);
226
+ if (info.type !== "File") {
227
+ return yield* new SubagentContentError({
228
+ reason: "reference-invalid",
229
+ source,
230
+ detail: `Source "${source}" must be a regular file.`,
231
+ });
232
+ }
233
+ return yield* fs.readFileString(realSource);
234
+ }).pipe(Effect.mapError((cause) => cause instanceof SubagentContentError
235
+ ? cause
236
+ : new SubagentContentError({
237
+ reason: "reference-invalid",
238
+ source,
239
+ detail: `Source "${source}" could not be read.`,
240
+ cause,
241
+ })));
242
+ const manifestSource = yield* readFile(MANIFEST_FILENAME);
243
+ const manifest = yield* Effect.try({
244
+ try: () => JSON.parse(manifestSource),
245
+ catch: () => new SubagentContentError({
246
+ reason: "manifest-invalid",
247
+ source: MANIFEST_FILENAME,
248
+ detail: "Subagent manifest must contain valid JSON.",
77
249
  }),
78
250
  });
79
- if (!isPlainObject(parsed.frontmatter)) {
80
- return yield* new SubagentContentError({
81
- reason: "invalid-frontmatter",
82
- detail: "Subagent frontmatter must be a YAML mapping",
83
- suggestion: `Add YAML frontmatter with name: ${expectedName}.`,
84
- });
251
+ return yield* readSubagentPackage({ manifest, readFile });
252
+ });
253
+ /** Select one explicit implementation; a complete native definition bypasses the core. */
254
+ export const selectSubagentImplementation = (pkg, agentId) => {
255
+ const implementation = pkg.implementations[agentId];
256
+ if (implementation?.kind === "native") {
257
+ return { ...implementation, sourceDependencies: [MANIFEST_FILENAME, implementation.source] };
85
258
  }
86
- const fm = parsed.frontmatter;
87
- const name = fm["name"];
88
- if (name !== expectedName) {
89
- return yield* new SubagentContentError({
90
- reason: "name-mismatch",
91
- detail: `Subagent frontmatter name "${String(name)}" does not match expected name "${expectedName}"`,
92
- suggestion: `Set subagent.json name, frontmatter name, and filename to ${expectedName}.`,
93
- });
259
+ const core = pkg.core;
260
+ if (core === undefined) {
261
+ return {
262
+ kind: "unsupported",
263
+ reason: `This package has no implementation for ${agentId}.`,
264
+ sourceDependencies: [MANIFEST_FILENAME],
265
+ };
94
266
  }
267
+ const customization = implementation?.kind === "customized" ? implementation : undefined;
268
+ const replacement = customization?.instructions;
269
+ const instructions = replacement === undefined
270
+ ? core.instructions
271
+ : replacement.mode === "replace"
272
+ ? replacement.content
273
+ : `${core.instructions}\n\n${replacement.content}`;
95
274
  return {
96
- frontmatter: Option.some(fm),
97
- agentOverrides: extractAgentOverrides(fm),
98
- body: parsed.body,
275
+ kind: customization === undefined ? "core" : "customized",
276
+ name: core.name,
277
+ description: core.description,
278
+ instructions,
279
+ configuration: customization?.configuration ?? {},
280
+ sourceDependencies: [
281
+ ...new Set([
282
+ MANIFEST_FILENAME,
283
+ ...(replacement?.mode === "replace" ? [] : [core.source]),
284
+ ...(replacement === undefined ? [] : [replacement.source]),
285
+ ]),
286
+ ],
99
287
  };
100
- });
288
+ };
101
289
  //# sourceMappingURL=subagent-content.js.map
@@ -12,13 +12,13 @@
12
12
  */
13
13
  export { AGENT_SKILLS_COMPATIBILITY_MAX_LENGTH, AGENT_SKILLS_DESCRIPTION_MAX_LENGTH, AGENT_SKILLS_FRONTMATTER_FIELDS, AGENT_SKILLS_NAME_MAX_LENGTH, AGENT_SKILLS_REFERENCE_VALIDATOR_URL, AGENT_SKILLS_SPECIFICATION_URL, AGENT_SKILLS_STANDARD_REVISION, } from "./content/agent-skills-standard.js";
14
14
  export { FRONTMATTER_PARSE_FALLBACK_REASON, FrontmatterParseFailure, type FrontmatterResult, parseFrontmatterEffect, parseFrontmatterSync, } from "./content/frontmatter.js";
15
- export { type SkillFrontmatter, SkillFrontmatterSchema, type SkillFrontmatterValidation, parseSkillMd, validateSkillFrontmatter, } from "./content/skill-content.js";
15
+ export { type ExtractedSkillMetadata, type SkillFrontmatter, SkillFrontmatterSchema, type SkillFrontmatterValidation, parseSkillMd, extractSkillMetadata, validateSkillFrontmatter, } from "./content/skill-content.js";
16
16
  export { type Skill } from "./content/skill-types.js";
17
- export { type SubagentAgentOverrides, type SubagentContentResult, SubagentContentError, parseSubagentMd, } from "./content/subagent-content.js";
18
- export { ArchiveGuardrailError, type ArchiveGuardrailLimits, ZIP_LOCAL_SIGNATURE, type ZipEntry, checkForbiddenSourceEntries, parseZipCentralDirectory, validateArchive, } from "./packaging/archive-guardrails.js";
17
+ export { type DecodedSubagentPackage, type NativeSubagentContent, type ResolvedSubagentImplementation, type SelectedSubagentImplementation, SubagentContentError, loadSubagentPackage, nativeSubagentFormat, parseNativeSubagent, readSubagentPackage, selectSubagentImplementation, } from "./content/subagent-content.js";
18
+ export { ArchiveGuardrailError, type ArchiveGuardrailLimits, ZIP_LOCAL_SIGNATURE, type ZipEntry, checkForbiddenSourceEntries, defaultReadEntry, parseZipCentralDirectory, validateArchive, } from "./packaging/archive-guardrails.js";
19
19
  export { FilteredPackageError, type ValidateFilteredPackageArgs, validateFilteredPackage, } from "./packaging/filtered-package-validation.js";
20
20
  export { IngestLimitError, IngestUnsupportedContentTypeError, REGISTRY_PUBLISH_MAX_ARCHIVE_BYTES, REGISTRY_PUBLISH_MAX_REQUEST_BYTES, enforceArchiveContentType, enforceArchiveSizeLimit, enforceRequestSizeLimit, } from "./packaging/ingest-limits.js";
21
- export { type NormalizePublishInputArgs, type PublishArchiveInput, type PublishInput, defaultReadEntry, normalizePublishInput, } from "./packaging/input-normalization.js";
21
+ export { type NormalizePublishInputArgs, type PublishArchiveInput, type PublishInput, normalizePublishInput, } from "./packaging/input-normalization.js";
22
22
  export { type DeclaredPublishIdentity, type ExtensionManifest, MANIFEST_FILENAME_BY_TYPE, MANIFEST_FILENAMES, MANIFEST_SCHEMA_BY_TYPE, ManifestError, type ManifestIdentity, ManifestIdentitySchema, type ManifestResolutionInput, type ResolvedManifest, manifestFilenameForType, extensionTypeForManifestFilename, manifestSchemaForType, resolveManifest, validateDeclaredManifestAlignment, validateManifestHasNoAgentsField, } from "./packaging/manifest-policy.js";
23
23
  export { readExtensionManifest } from "./packaging/manifest-file.js";
24
24
  //# sourceMappingURL=index.d.ts.map