@ai-outfitter/outfitter 0.9.0 → 0.10.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 (77) hide show
  1. package/.outfitter/skills/outfitter/SKILL.md +86 -0
  2. package/README.md +2 -0
  3. package/code/pi-extension/src/outfitter-extension.js +11 -0
  4. package/dist/agents/AdapterProfileControls.js +2 -2
  5. package/dist/agents/AdapterProfileControls.js.map +1 -1
  6. package/dist/agents/AgentAdapter.d.ts +0 -2
  7. package/dist/agents/LaunchResources.d.ts +4 -0
  8. package/dist/agents/LaunchResources.js +27 -1
  9. package/dist/agents/LaunchResources.js.map +1 -1
  10. package/dist/agents/OutfitterSkill.d.ts +11 -0
  11. package/dist/agents/OutfitterSkill.js +128 -0
  12. package/dist/agents/OutfitterSkill.js.map +1 -0
  13. package/dist/agents/claude/ClaudeAdapter.js +16 -1
  14. package/dist/agents/claude/ClaudeAdapter.js.map +1 -1
  15. package/dist/agents/pi/PiAdapter.js +4 -12
  16. package/dist/agents/pi/PiAdapter.js.map +1 -1
  17. package/dist/agents/pi/PiArgs.js +1 -1
  18. package/dist/agents/pi/PiArgs.js.map +1 -1
  19. package/dist/agents/pi/PiSkillSources.js +5 -1
  20. package/dist/agents/pi/PiSkillSources.js.map +1 -1
  21. package/dist/cli/OutfitterCli.js +0 -2
  22. package/dist/cli/OutfitterCli.js.map +1 -1
  23. package/dist/cli/commands/PiLoginLaunch.d.ts +5 -0
  24. package/dist/cli/commands/PiLoginLaunch.js +2 -0
  25. package/dist/cli/commands/PiLoginLaunch.js.map +1 -1
  26. package/dist/cli/commands/RunCommand.d.ts +0 -2
  27. package/dist/cli/commands/RunCommand.js +29 -9
  28. package/dist/cli/commands/RunCommand.js.map +1 -1
  29. package/dist/cli/commands/profile/LintCommand.js +33 -1
  30. package/dist/cli/commands/profile/LintCommand.js.map +1 -1
  31. package/dist/cli/commands/run/RunProfileResolution.d.ts +2 -0
  32. package/dist/cli/commands/run/RunProfileResolution.js +4 -0
  33. package/dist/cli/commands/run/RunProfileResolution.js.map +1 -1
  34. package/dist/profiles/Profile.d.ts +14 -1
  35. package/dist/profiles/Profile.js.map +1 -1
  36. package/dist/profiles/ProfileLoader.js +18 -2
  37. package/dist/profiles/ProfileLoader.js.map +1 -1
  38. package/dist/profiles/ProfileMerger.js +3 -2
  39. package/dist/profiles/ProfileMerger.js.map +1 -1
  40. package/dist/schemas/profile.schema.json +40 -3
  41. package/dist/skills/ProfileSkillResolution.d.ts +21 -0
  42. package/dist/skills/ProfileSkillResolution.js +88 -0
  43. package/dist/skills/ProfileSkillResolution.js.map +1 -0
  44. package/dist/skills/SkillCatalog.d.ts +41 -0
  45. package/dist/skills/SkillCatalog.js +119 -0
  46. package/dist/skills/SkillCatalog.js.map +1 -0
  47. package/dist/skills/SkillDocument.d.ts +21 -0
  48. package/dist/skills/SkillDocument.js +85 -0
  49. package/dist/skills/SkillDocument.js.map +1 -0
  50. package/dist/skills/SkillResolution.d.ts +34 -0
  51. package/dist/skills/SkillResolution.js +217 -0
  52. package/dist/skills/SkillResolution.js.map +1 -0
  53. package/{doc → docs}/documentation/README.md +4 -1
  54. package/docs/documentation/actions.md +97 -0
  55. package/docs/documentation/best-practices.md +105 -0
  56. package/{doc → docs}/documentation/profile-repository.md +74 -6
  57. package/docs/documentation/skills.md +436 -0
  58. package/{doc → docs}/documentation/support-matrix.md +4 -1
  59. package/package.json +4 -8
  60. package/src/schemas/profile.schema.json +40 -3
  61. package/dist/agents/OutfitterDocs.d.ts +0 -2
  62. package/dist/agents/OutfitterDocs.js +0 -38
  63. package/dist/agents/OutfitterDocs.js.map +0 -1
  64. package/skills/outfitter/SKILL.md +0 -68
  65. /package/{doc → docs}/architecture/state_writeback_strategy.md +0 -0
  66. /package/{doc → docs}/documentation/cli.md +0 -0
  67. /package/{doc → docs}/documentation/concepts.md +0 -0
  68. /package/{doc → docs}/documentation/first-time-cli-agent-users.md +0 -0
  69. /package/{doc → docs}/documentation/getting-started.md +0 -0
  70. /package/{doc → docs}/documentation/iterating-on-profiles.md +0 -0
  71. /package/{doc → docs}/documentation/profiles.md +0 -0
  72. /package/{doc → docs}/documentation/state.md +0 -0
  73. /package/{doc → docs}/documentation/switching-to-outfitter.md +0 -0
  74. /package/{doc → docs}/documentation/usecases/engineering.md +0 -0
  75. /package/{doc → docs}/documentation/usecases/organization-profile-catalog.md +0 -0
  76. /package/{doc → docs}/documentation/usecases/persona-reviews.md +0 -0
  77. /package/{doc → docs}/philosophy.md +0 -0
@@ -42,7 +42,7 @@
42
42
  },
43
43
  "skills": {
44
44
  "type": "array",
45
- "items": { "type": "string" }
45
+ "items": { "$ref": "#/$defs/skillEntry" }
46
46
  },
47
47
  "prompt_template": { "type": "string" },
48
48
  "system_prompt": { "type": "string" },
@@ -81,7 +81,7 @@
81
81
  },
82
82
  "skills": {
83
83
  "type": "array",
84
- "items": { "type": "string" }
84
+ "items": { "$ref": "#/$defs/skillEntry" }
85
85
  },
86
86
  "prompt_template": { "type": "string" },
87
87
  "system_prompt": { "type": "string" },
@@ -111,7 +111,7 @@
111
111
  },
112
112
  "skills": {
113
113
  "type": "array",
114
- "items": { "type": "string" }
114
+ "items": { "$ref": "#/$defs/skillEntry" }
115
115
  },
116
116
  "prompt_template": { "type": "string" },
117
117
  "system_prompt": { "type": "string" },
@@ -129,6 +129,43 @@
129
129
  },
130
130
  "additionalProperties": true,
131
131
  "$defs": {
132
+ "skillReference": {
133
+ "oneOf": [
134
+ {
135
+ "type": "object",
136
+ "required": ["file"],
137
+ "properties": {
138
+ "file": { "type": "string" }
139
+ },
140
+ "additionalProperties": false
141
+ },
142
+ {
143
+ "type": "object",
144
+ "required": ["repo_file"],
145
+ "properties": {
146
+ "repo_file": { "type": "string" }
147
+ },
148
+ "additionalProperties": false
149
+ }
150
+ ]
151
+ },
152
+ "skillEntry": {
153
+ "oneOf": [
154
+ { "type": "string" },
155
+ {
156
+ "type": "object",
157
+ "required": ["id"],
158
+ "properties": {
159
+ "id": { "type": "string" },
160
+ "references": {
161
+ "type": "array",
162
+ "items": { "$ref": "#/$defs/skillReference" }
163
+ }
164
+ },
165
+ "additionalProperties": false
166
+ }
167
+ ]
168
+ },
132
169
  "appendSystemPromptEntry": {
133
170
  "oneOf": [
134
171
  { "type": "string" },
@@ -0,0 +1,21 @@
1
+ import type { LoadedProfile } from '../profiles/ProfileLoader.js';
2
+ import type { Profile } from '../profiles/Profile.js';
3
+ import { type SkillResolutionDiagnostic } from './SkillResolution.js';
4
+ export interface ProfileSkillResolutionInput {
5
+ readonly profile: Profile;
6
+ readonly profileLayers: readonly LoadedProfile[];
7
+ /** Materialized configured profile-source paths, in configured order. */
8
+ readonly sourcePaths: readonly string[];
9
+ readonly projectDirectory: string;
10
+ /** Composite profile root; generated skills are written beneath `outfitter/skills/`. */
11
+ readonly outputDirectory?: string;
12
+ }
13
+ export interface ProfileSkillResolutionResult {
14
+ readonly profile: Profile;
15
+ readonly diagnostics: readonly SkillResolutionDiagnostic[];
16
+ }
17
+ /**
18
+ * Resolves catalog skill IDs in `controls.skills` (generic and adapter-scoped) to
19
+ * generated skill directories, so adapters keep receiving plain path sources.
20
+ */
21
+ export declare const resolveProfileSkillControls: (input: ProfileSkillResolutionInput) => ProfileSkillResolutionResult;
@@ -0,0 +1,88 @@
1
+ // Resolves a profile's selected skill IDs into generated skill sources before adapter launch.
2
+ import { join } from 'node:path';
3
+ import { inferProfileIncludeSourceRoot } from '../profiles/PromptIncludes.js';
4
+ import { skillControlEntryIdentity } from '../agents/LaunchResources.js';
5
+ import { discoverSkillCatalog } from './SkillCatalog.js';
6
+ import { resolveSkillEntries, } from './SkillResolution.js';
7
+ /**
8
+ * Resolves catalog skill IDs in `controls.skills` (generic and adapter-scoped) to
9
+ * generated skill directories, so adapters keep receiving plain path sources.
10
+ */
11
+ export const resolveProfileSkillControls = (input) => {
12
+ const catalog = discoverSkillCatalog({
13
+ projectDirectory: input.projectDirectory,
14
+ profileLayers: input.profileLayers,
15
+ sourcePaths: input.sourcePaths,
16
+ });
17
+ const diagnostics = [];
18
+ const outputDirectory = input.outputDirectory === undefined ? undefined : join(input.outputDirectory, 'outfitter', 'skills');
19
+ // One skill ID materializes once; selecting it from several control scopes reuses
20
+ // the same generated directory, and conflicting reference sets are an error.
21
+ const materializationCache = new Map();
22
+ const resolveEntries = (entries, scope) => {
23
+ const resolution = resolveSkillEntries({
24
+ entries: entries.map((entry) => toSkillEntryInput(entry, scope, input.profileLayers)),
25
+ catalog,
26
+ projectDirectory: input.projectDirectory,
27
+ outputDirectory,
28
+ materializationCache,
29
+ });
30
+ diagnostics.push(...resolution.diagnostics);
31
+ return resolution.sources;
32
+ };
33
+ const controls = resolveSkillControls(input.profile.controls, resolveEntries);
34
+ diagnostics.push(...createShadowedSkillDiagnostics(catalog, input.profile.controls));
35
+ return { profile: { ...input.profile, controls }, diagnostics };
36
+ };
37
+ const resolveSkillControls = (controls, resolveEntries) => ({
38
+ ...controls,
39
+ ...(controls.skills === undefined ? {} : { skills: resolveEntries(controls.skills, 'skills') }),
40
+ ...(controls.pi?.skills === undefined
41
+ ? {}
42
+ : { pi: { ...controls.pi, skills: resolveEntries(controls.pi.skills, 'pi') } }),
43
+ ...(controls.claude?.skills === undefined
44
+ ? {}
45
+ : { claude: { ...controls.claude, skills: resolveEntries(controls.claude.skills, 'claude') } }),
46
+ });
47
+ /**
48
+ * Profile-added `file` references resolve from the repository containing the
49
+ * declaring profile. The lookup stays within the entry's own control scope so an
50
+ * object selection in another scope cannot supply the wrong repository root.
51
+ */
52
+ const toSkillEntryInput = (entry, scope, profileLayers) => {
53
+ if (typeof entry === 'string') {
54
+ return { entry };
55
+ }
56
+ const identity = skillControlEntryIdentity(entry);
57
+ const declaringLayer = [...profileLayers]
58
+ .reverse()
59
+ .find((layer) => readScopedSkillEntries(layer.profile.controls, scope).some((layerEntry) => typeof layerEntry !== 'string' && skillControlEntryIdentity(layerEntry) === identity));
60
+ return {
61
+ entry,
62
+ profileReferenceRoot: declaringLayer === undefined
63
+ ? undefined
64
+ : inferProfileIncludeSourceRoot({
65
+ sourceRootPath: declaringLayer.sourceRootPath,
66
+ profilePath: declaringLayer.profilePath,
67
+ }),
68
+ };
69
+ };
70
+ const readScopedSkillEntries = (controls, scope) => (scope === 'skills' ? controls.skills : controls[scope]?.skills) ?? [];
71
+ const readLayerSkillEntries = (controls) => [
72
+ ...(controls.skills ?? []),
73
+ ...(controls.pi?.skills ?? []),
74
+ ...(controls.claude?.skills ?? []),
75
+ ];
76
+ /** Reports shadowing only for IDs the profile actually selects. */
77
+ const createShadowedSkillDiagnostics = (catalog, controls) => {
78
+ const selectedIds = new Set(readLayerSkillEntries(controls).map((entry) => (typeof entry === 'string' ? entry : entry.id)));
79
+ return catalog.shadowed
80
+ .filter((shadow) => selectedIds.has(shadow.selected.id))
81
+ .map((shadow) => ({
82
+ severity: 'warning',
83
+ path: shadow.shadowedDirectory,
84
+ message: `Skill ID '${shadow.selected.id}' is supplied by ${shadow.selected.origin} ('${shadow.selected.directory}') ` +
85
+ `and shadows '${shadow.shadowedDirectory}'.`,
86
+ }));
87
+ };
88
+ //# sourceMappingURL=ProfileSkillResolution.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProfileSkillResolution.js","sourceRoot":"","sources":["../../src/skills/ProfileSkillResolution.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ export interface SkillCatalogRoot {
2
+ /** Directory scanned for `<skill-id>/SKILL.md` entries. */
3
+ readonly skillsDirectory: string;
4
+ /** Root that skill `file` references resolve from (the repository containing the skill). */
5
+ readonly referenceRoot: string;
6
+ /** Human-readable origin for shadowing diagnostics. */
7
+ readonly origin: string;
8
+ }
9
+ export interface SkillCatalogEntry {
10
+ readonly id: string;
11
+ readonly directory: string;
12
+ readonly skillPath: string;
13
+ readonly referenceRoot: string;
14
+ readonly origin: string;
15
+ }
16
+ export interface SkillCatalog {
17
+ readonly entries: ReadonlyMap<string, SkillCatalogEntry>;
18
+ readonly shadowed: readonly {
19
+ readonly selected: SkillCatalogEntry;
20
+ readonly shadowedDirectory: string;
21
+ }[];
22
+ }
23
+ export interface SkillCatalogInput {
24
+ readonly projectDirectory?: string;
25
+ /** Directory-profile resource roots contributing bundled `skills/` folders. */
26
+ readonly profileLayers?: readonly {
27
+ readonly sourceRootPath?: string;
28
+ readonly resourceRootPath?: string;
29
+ }[];
30
+ /** Materialized configured profile-source paths, in configured (precedence) order. */
31
+ readonly sourcePaths?: readonly string[];
32
+ }
33
+ /**
34
+ * Builds the skill ID catalog following layer precedence: project-local, project,
35
+ * bundled directory-profile skills (highest-precedence layer first), then
36
+ * configured sources (later entries outrank earlier ones, matching profile
37
+ * precedence). The first root supplying an ID wins; later roots are reported as
38
+ * shadowed.
39
+ */
40
+ export declare const discoverSkillCatalog: (input: SkillCatalogInput) => SkillCatalog;
41
+ export declare const createSkillCatalogRoots: (input: SkillCatalogInput) => readonly SkillCatalogRoot[];
@@ -0,0 +1,119 @@
1
+ // Discovers catalog skills across project, directory-profile, and configured-source roots.
2
+ import { existsSync, readdirSync, statSync } from 'node:fs';
3
+ import { basename, dirname, join } from 'node:path';
4
+ import { inferProfileIncludeSourceRoot } from '../profiles/PromptIncludes.js';
5
+ import { isValidSkillId } from './SkillDocument.js';
6
+ /**
7
+ * Builds the skill ID catalog following layer precedence: project-local, project,
8
+ * bundled directory-profile skills (highest-precedence layer first), then
9
+ * configured sources (later entries outrank earlier ones, matching profile
10
+ * precedence). The first root supplying an ID wins; later roots are reported as
11
+ * shadowed.
12
+ */
13
+ export const discoverSkillCatalog = (input) => {
14
+ const entries = new Map();
15
+ const shadowed = [];
16
+ for (const root of createSkillCatalogRoots(input)) {
17
+ for (const entry of readSkillCatalogRootEntries(root)) {
18
+ const selected = entries.get(entry.id);
19
+ if (selected === undefined) {
20
+ entries.set(entry.id, entry);
21
+ }
22
+ else if (selected.directory !== entry.directory) {
23
+ shadowed.push({ selected, shadowedDirectory: entry.directory });
24
+ }
25
+ }
26
+ }
27
+ return { entries, shadowed };
28
+ };
29
+ export const createSkillCatalogRoots = (input) => {
30
+ const roots = [];
31
+ if (input.projectDirectory !== undefined) {
32
+ roots.push({
33
+ skillsDirectory: join(input.projectDirectory, '.outfitter', 'local', 'skills'),
34
+ referenceRoot: input.projectDirectory,
35
+ origin: 'project-local skills',
36
+ }, {
37
+ skillsDirectory: join(input.projectDirectory, '.outfitter', 'skills'),
38
+ referenceRoot: input.projectDirectory,
39
+ origin: 'project skills',
40
+ });
41
+ }
42
+ // Profile layers arrive lowest-precedence first (see ProfileMerger); reverse so
43
+ // the selected profile's bundled skill outranks an inherited base profile's.
44
+ for (const layer of [...(input.profileLayers ?? [])].reverse()) {
45
+ if (layer.resourceRootPath === undefined) {
46
+ continue;
47
+ }
48
+ roots.push({
49
+ skillsDirectory: join(layer.resourceRootPath, 'skills'),
50
+ referenceRoot: inferProfileIncludeSourceRoot({
51
+ sourceRootPath: layer.sourceRootPath,
52
+ profilePath: join(layer.resourceRootPath, 'profile.yml'),
53
+ /* v8 ignore next -- directory profiles always sit under a source root. */
54
+ }) ?? layer.resourceRootPath,
55
+ origin: `profile '${basename(layer.resourceRootPath)}' skills`,
56
+ });
57
+ }
58
+ // Later configured sources outrank earlier ones for profiles; mirror that here.
59
+ for (const sourcePath of [...(input.sourcePaths ?? [])].reverse()) {
60
+ const catalogRoot = resolveCatalogRoot(sourcePath);
61
+ roots.push({
62
+ skillsDirectory: join(catalogRoot, 'skills'),
63
+ referenceRoot: basename(catalogRoot) === '.outfitter' ? dirname(catalogRoot) : catalogRoot,
64
+ origin: `source '${sourcePath}'`,
65
+ });
66
+ }
67
+ return dedupeSkillCatalogRoots(roots);
68
+ };
69
+ /**
70
+ * The catalog root is the directory containing `profiles/`. A source path that
71
+ * points directly at the profiles directory resolves to its parent.
72
+ */
73
+ const resolveCatalogRoot = (sourcePath) => basename(sourcePath) === 'profiles' ? dirname(sourcePath) : sourcePath;
74
+ const dedupeSkillCatalogRoots = (roots) => {
75
+ const seen = new Set();
76
+ return roots.filter((root) => {
77
+ if (seen.has(root.skillsDirectory)) {
78
+ return false;
79
+ }
80
+ seen.add(root.skillsDirectory);
81
+ return true;
82
+ });
83
+ };
84
+ const readSkillCatalogRootEntries = (root) => readOptionalDirectoryNames(root.skillsDirectory)
85
+ .filter((name) => isValidSkillId(name) && existsSync(join(root.skillsDirectory, name, 'SKILL.md')))
86
+ .sort()
87
+ .map((name) => ({
88
+ id: name,
89
+ directory: join(root.skillsDirectory, name),
90
+ skillPath: join(root.skillsDirectory, name, 'SKILL.md'),
91
+ referenceRoot: root.referenceRoot,
92
+ origin: root.origin,
93
+ }));
94
+ const readOptionalDirectoryNames = (directory) => {
95
+ try {
96
+ return readdirSync(directory, { withFileTypes: true })
97
+ .filter((entry) => entry.isDirectory() || isSymlinkedDirectory(join(directory, entry.name), entry.isSymbolicLink()))
98
+ .map((entry) => entry.name);
99
+ }
100
+ catch (error) {
101
+ if (isMissingPathError(error)) {
102
+ return [];
103
+ }
104
+ throw new Error(`Could not read skills directory '${directory}': ${String(error)}`, { cause: error });
105
+ }
106
+ };
107
+ const isSymlinkedDirectory = (path, isSymbolicLink) => {
108
+ if (!isSymbolicLink) {
109
+ return false;
110
+ }
111
+ try {
112
+ return statSync(path).isDirectory();
113
+ }
114
+ catch {
115
+ return false;
116
+ }
117
+ };
118
+ const isMissingPathError = (error) => error !== null && typeof error === 'object' && 'code' in error && error.code === 'ENOENT';
119
+ //# sourceMappingURL=SkillCatalog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SkillCatalog.js","sourceRoot":"","sources":["../../src/skills/SkillCatalog.ts"],"names":[],"mappings":"AAAA,2FAA2F;AAC3F,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC5D,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEpD,OAAO,EAAE,6BAA6B,EAAE,MAAM,+BAA+B,CAAC;AAC9E,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAgCpD;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,KAAwB,EAAgB,EAAE;IAC7E,MAAM,OAAO,GAAG,IAAI,GAAG,EAA6B,CAAC;IACrD,MAAM,QAAQ,GAAmF,EAAE,CAAC;IAEpG,KAAK,MAAM,IAAI,IAAI,uBAAuB,CAAC,KAAK,CAAC,EAAE,CAAC;QAClD,KAAK,MAAM,KAAK,IAAI,2BAA2B,CAAC,IAAI,CAAC,EAAE,CAAC;YACtD,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YAEvC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;gBAC3B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;YAC/B,CAAC;iBAAM,IAAI,QAAQ,CAAC,SAAS,KAAK,KAAK,CAAC,SAAS,EAAE,CAAC;gBAClD,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,iBAAiB,EAAE,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC;YAClE,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC;AAC/B,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,KAAwB,EAA+B,EAAE;IAC/F,MAAM,KAAK,GAAuB,EAAE,CAAC;IAErC,IAAI,KAAK,CAAC,gBAAgB,KAAK,SAAS,EAAE,CAAC;QACzC,KAAK,CAAC,IAAI,CACR;YACE,eAAe,EAAE,IAAI,CAAC,KAAK,CAAC,gBAAgB,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,CAAC;YAC9E,aAAa,EAAE,KAAK,CAAC,gBAAgB;YACrC,MAAM,EAAE,sBAAsB;SAC/B,EACD;YACE,eAAe,EAAE,IAAI,CAAC,KAAK,CAAC,gBAAgB,EAAE,YAAY,EAAE,QAAQ,CAAC;YACrE,aAAa,EAAE,KAAK,CAAC,gBAAgB;YACrC,MAAM,EAAE,gBAAgB;SACzB,CACF,CAAC;IACJ,CAAC;IAED,gFAAgF;IAChF,6EAA6E;IAC7E,KAAK,MAAM,KAAK,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,aAAa,IAAI,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;QAC/D,IAAI,KAAK,CAAC,gBAAgB,KAAK,SAAS,EAAE,CAAC;YACzC,SAAS;QACX,CAAC;QAED,KAAK,CAAC,IAAI,CAAC;YACT,eAAe,EAAE,IAAI,CAAC,KAAK,CAAC,gBAAgB,EAAE,QAAQ,CAAC;YACvD,aAAa,EACX,6BAA6B,CAAC;gBAC5B,cAAc,EAAE,KAAK,CAAC,cAAc;gBACpC,WAAW,EAAE,IAAI,CAAC,KAAK,CAAC,gBAAgB,EAAE,aAAa,CAAC;gBACxD,0EAA0E;aAC3E,CAAC,IAAI,KAAK,CAAC,gBAAgB;YAC9B,MAAM,EAAE,YAAY,QAAQ,CAAC,KAAK,CAAC,gBAAgB,CAAC,UAAU;SAC/D,CAAC,CAAC;IACL,CAAC;IAED,gFAAgF;IAChF,KAAK,MAAM,UAAU,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;QAClE,MAAM,WAAW,GAAG,kBAAkB,CAAC,UAAU,CAAC,CAAC;QACnD,KAAK,CAAC,IAAI,CAAC;YACT,eAAe,EAAE,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC;YAC5C,aAAa,EAAE,QAAQ,CAAC,WAAW,CAAC,KAAK,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,WAAW;YAC1F,MAAM,EAAE,WAAW,UAAU,GAAG;SACjC,CAAC,CAAC;IACL,CAAC;IAED,OAAO,uBAAuB,CAAC,KAAK,CAAC,CAAC;AACxC,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,kBAAkB,GAAG,CAAC,UAAkB,EAAU,EAAE,CACxD,QAAQ,CAAC,UAAU,CAAC,KAAK,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC;AAEzE,MAAM,uBAAuB,GAAG,CAAC,KAAkC,EAA+B,EAAE;IAClG,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAE/B,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE;QAC3B,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,eAAe,CAAC,EAAE,CAAC;YACnC,OAAO,KAAK,CAAC;QACf,CAAC;QAED,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QAC/B,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC;AACL,CAAC,CAAC;AAEF,MAAM,2BAA2B,GAAG,CAAC,IAAsB,EAAgC,EAAE,CAC3F,0BAA0B,CAAC,IAAI,CAAC,eAAe,CAAC;KAC7C,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC;KAClG,IAAI,EAAE;KACN,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IACd,EAAE,EAAE,IAAI;IACR,SAAS,EAAE,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,IAAI,CAAC;IAC3C,SAAS,EAAE,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,IAAI,EAAE,UAAU,CAAC;IACvD,aAAa,EAAE,IAAI,CAAC,aAAa;IACjC,MAAM,EAAE,IAAI,CAAC,MAAM;CACpB,CAAC,CAAC,CAAC;AAER,MAAM,0BAA0B,GAAG,CAAC,SAAiB,EAAqB,EAAE;IAC1E,IAAI,CAAC;QACH,OAAO,WAAW,CAAC,SAAS,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC;aACnD,MAAM,CACL,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,IAAI,oBAAoB,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC,cAAc,EAAE,CAAC,CAC5G;aACA,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,kBAAkB,CAAC,KAAK,CAAC,EAAE,CAAC;YAC9B,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,MAAM,IAAI,KAAK,CAAC,oCAAoC,SAAS,MAAM,MAAM,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;IACxG,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,oBAAoB,GAAG,CAAC,IAAY,EAAE,cAAuB,EAAW,EAAE;IAC9E,IAAI,CAAC,cAAc,EAAE,CAAC;QACpB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,CAAC;QACH,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,kBAAkB,GAAG,CAAC,KAAc,EAAW,EAAE,CACrD,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC"}
@@ -0,0 +1,21 @@
1
+ import type { SkillReference } from '../profiles/Profile.js';
2
+ export declare const isSkillReference: (value: unknown) => value is SkillReference;
3
+ /** Frontmatter keys that materialize external files into a generated skill directory. */
4
+ export declare const skillMaterializationSections: readonly ["references", "scripts", "assets"];
5
+ export type SkillMaterializationSection = (typeof skillMaterializationSections)[number];
6
+ export interface SkillDocument {
7
+ readonly name: string;
8
+ readonly description?: string;
9
+ readonly references: readonly SkillReference[];
10
+ readonly scripts: readonly SkillReference[];
11
+ readonly assets: readonly SkillReference[];
12
+ }
13
+ export interface SkillDocumentIssue {
14
+ readonly path: string;
15
+ readonly message: string;
16
+ }
17
+ /** Skill IDs and directory names: lowercase alphanumerics and single hyphens, at most 64 characters. */
18
+ export declare const isValidSkillId: (value: string) => boolean;
19
+ export declare const parseSkillDocument: (content: string, skillPath: string) => SkillDocument | SkillDocumentIssue;
20
+ export declare const readSkillDocument: (skillPath: string) => SkillDocument | SkillDocumentIssue;
21
+ export declare const isSkillDocumentIssue: (value: SkillDocument | SkillDocumentIssue) => value is SkillDocumentIssue;
@@ -0,0 +1,85 @@
1
+ // Parses and validates SKILL.md frontmatter for catalog skill resolution.
2
+ import { readFileSync } from 'node:fs';
3
+ import { parseYamlDocument } from '../validation/YamlDocument.js';
4
+ export const isSkillReference = (value) => value !== null &&
5
+ typeof value === 'object' &&
6
+ !Array.isArray(value) &&
7
+ Object.keys(value).length === 1 &&
8
+ (typeof value.file === 'string' ||
9
+ typeof value.repo_file === 'string');
10
+ const skillNamePattern = /^[a-z0-9]+(?:-[a-z0-9]+)*$/u;
11
+ const skillNameMaxLength = 64;
12
+ /** Frontmatter keys that materialize external files into a generated skill directory. */
13
+ export const skillMaterializationSections = ['references', 'scripts', 'assets'];
14
+ /** Skill IDs and directory names: lowercase alphanumerics and single hyphens, at most 64 characters. */
15
+ export const isValidSkillId = (value) => value.length <= skillNameMaxLength && skillNamePattern.test(value);
16
+ export const parseSkillDocument = (content, skillPath) => {
17
+ const frontmatter = parseSkillFrontmatter(content, skillPath);
18
+ if ('message' in frontmatter) {
19
+ return frontmatter;
20
+ }
21
+ const { name, description } = frontmatter.record;
22
+ if (typeof name !== 'string' || !isValidSkillId(name)) {
23
+ return {
24
+ path: skillPath,
25
+ message: `SKILL.md 'name' must use lowercase letters, numbers, and single hyphens ` +
26
+ `(max ${skillNameMaxLength} characters).`,
27
+ };
28
+ }
29
+ const sections = {};
30
+ for (const section of skillMaterializationSections) {
31
+ const entries = frontmatter.record[section];
32
+ if (entries !== undefined && (!Array.isArray(entries) || !entries.every(isSkillReference))) {
33
+ return {
34
+ path: skillPath,
35
+ message: `SKILL.md '${section}' entries must each contain exactly one 'file' or 'repo_file' source.`,
36
+ };
37
+ }
38
+ sections[section] = entries ?? [];
39
+ }
40
+ return {
41
+ name,
42
+ description: typeof description === 'string' ? description : undefined,
43
+ references: sections.references,
44
+ scripts: sections.scripts,
45
+ assets: sections.assets,
46
+ };
47
+ };
48
+ const parseSkillFrontmatter = (content, skillPath) => {
49
+ const frontmatter = extractFrontmatter(content);
50
+ if (frontmatter === undefined) {
51
+ return { path: skillPath, message: 'SKILL.md must start with a `---` YAML frontmatter block.' };
52
+ }
53
+ const parsed = parseYamlDocument(frontmatter, skillPath);
54
+ if (!parsed.ok) {
55
+ return { path: skillPath, message: `SKILL.md frontmatter is not valid YAML: ${parsed.issue.message}` };
56
+ }
57
+ const record = parsed.document;
58
+ if (record === null || typeof record !== 'object' || Array.isArray(record)) {
59
+ return { path: skillPath, message: 'SKILL.md frontmatter must be a YAML mapping.' };
60
+ }
61
+ return { record: record };
62
+ };
63
+ export const readSkillDocument = (skillPath) => {
64
+ let content;
65
+ try {
66
+ content = readFileSync(skillPath, 'utf8');
67
+ }
68
+ catch (error) {
69
+ return { path: skillPath, message: `Could not read SKILL.md: ${String(error)}` };
70
+ }
71
+ return parseSkillDocument(content, skillPath);
72
+ };
73
+ export const isSkillDocumentIssue = (value) => 'message' in value;
74
+ const extractFrontmatter = (content) => {
75
+ const lines = content.split('\n');
76
+ if (lines[0]?.trimEnd() !== '---') {
77
+ return undefined;
78
+ }
79
+ const closingIndex = lines.findIndex((line, index) => index > 0 && line.trimEnd() === '---');
80
+ if (closingIndex === -1) {
81
+ return undefined;
82
+ }
83
+ return lines.slice(1, closingIndex).join('\n');
84
+ };
85
+ //# sourceMappingURL=SkillDocument.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SkillDocument.js","sourceRoot":"","sources":["../../src/skills/SkillDocument.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import type { SkillControlEntry } from '../profiles/Profile.js';
2
+ import type { SkillCatalog } from './SkillCatalog.js';
3
+ export interface SkillResolutionDiagnostic {
4
+ readonly severity: 'error' | 'warning';
5
+ readonly path: string;
6
+ readonly message: string;
7
+ }
8
+ export interface SkillEntryInput {
9
+ readonly entry: SkillControlEntry;
10
+ /** Root for `file` references appended by the declaring profile. */
11
+ readonly profileReferenceRoot?: string;
12
+ }
13
+ /**
14
+ * Shared across control scopes so one skill ID materializes once; a second
15
+ * selection reuses the generated directory, and differing reference sets error.
16
+ */
17
+ export type SkillMaterializationCache = Map<string, {
18
+ readonly referencesKey: string;
19
+ readonly generatedDirectory?: string;
20
+ }>;
21
+ export interface SkillResolutionInput {
22
+ readonly entries: readonly SkillEntryInput[];
23
+ readonly catalog: SkillCatalog;
24
+ readonly projectDirectory?: string;
25
+ /** When set, resolved skills are copied here as `<id>/` with materialized references. */
26
+ readonly outputDirectory?: string;
27
+ readonly materializationCache?: SkillMaterializationCache;
28
+ }
29
+ export interface SkillResolutionResult {
30
+ /** Skill sources in entry order: generated directories for resolved IDs, original strings otherwise. */
31
+ readonly sources: readonly string[];
32
+ readonly diagnostics: readonly SkillResolutionDiagnostic[];
33
+ }
34
+ export declare const resolveSkillEntries: (input: SkillResolutionInput) => SkillResolutionResult;