@prismshadow/penguin-skills 0.0.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.
package/LICENSE ADDED
@@ -0,0 +1,201 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright [yyyy] [name of copyright owner]
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
package/README.md ADDED
@@ -0,0 +1,32 @@
1
+ # @prismshadow/penguin-skills
2
+
3
+ The PenguinHarness built-in skill library. A Skill is a directory with a `SKILL.md` (frontmatter: name, description, version, updated) — files are the runtime source of truth and ship raw in this package's npm tarball.
4
+
5
+ Skills follow the "index first, body on demand" design: only their metadata is injected into an Agent's system prompt; the Agent reads the full `SKILL.md` via shell when it actually needs it.
6
+
7
+ Included skills:
8
+
9
+ | Group | Skills |
10
+ | --- | --- |
11
+ | Agent Development | `agent-creation`, `benchmark-design`, `agent-evaluation`, `agent-optimization` |
12
+ | Data Analysis | `data-analysis` |
13
+ | Penguin Development | `penguin-sdk`, `penguin-cli`, `agenthub-models` |
14
+ | Web Development | `web-design` |
15
+ | Software Engineering | `software-engineering` |
16
+
17
+ The first group powers the self-improvement loop: design a Benchmark, evaluate the Target Agent, optimize it to version N+1 with a snapshot before every round.
18
+
19
+ ## Documentation
20
+
21
+ - [Skills](https://penguin.ooo/docs/skills)
22
+ - [Self-Improvement](https://penguin.ooo/docs/self-improvement)
23
+
24
+ ## Development
25
+
26
+ ```bash
27
+ pnpm --filter @prismshadow/penguin-skills build # tsup → dist/ (loader API)
28
+ pnpm --filter @prismshadow/penguin-skills typecheck
29
+ pnpm --filter @prismshadow/penguin-skills test
30
+ ```
31
+
32
+ Part of [PenguinHarness](https://github.com/Prism-Shadow/penguin-harness) · Apache-2.0
@@ -0,0 +1,61 @@
1
+ /** Skill's frontmatter metadata (four fields: name / description / version / updated; description itself is English-only, may also carry description_zh and short_description(_zh)). */
2
+ interface SkillMetadata {
3
+ /** Skill name (matches its containing directory name). */
4
+ name: string;
5
+ /** One-line description; injected into the model prompt via `{{SKILL_METADATA}}`. */
6
+ description: string;
7
+ /** UI short description (frontmatter `short_description`, optional): preferred in compact spots like cards, falls back to the full description if missing; not injected into the prompt. */
8
+ shortDescription?: string;
9
+ /** Chinese short description (frontmatter `short_description_zh`, optional). */
10
+ shortDescriptionZh?: string;
11
+ /** Version number (natural number); falls back to 1 on parse failure. */
12
+ version: number;
13
+ /** Update date (YYYY-MM-DD); defaults to "". */
14
+ updated: string;
15
+ }
16
+ /** A Skill in the library: metadata + full SKILL.md content (including frontmatter, written as-is on install). */
17
+ interface LibrarySkill extends SkillMetadata {
18
+ content: string;
19
+ /** Optional raw `icon.svg` content in the directory (custom icon, the file is the sole source, copied alongside SKILL.md on install); absent means none (frontend falls back to the default book icon). */
20
+ icon?: string;
21
+ }
22
+ /** Skill group manifest entry: group id, title (optionally with a Chinese title, displayed per UI language), and member Skill names. */
23
+ interface SkillGroupInfo {
24
+ id: string;
25
+ title: string;
26
+ /** Chinese group title (optional, displayed per UI language). */
27
+ titleZh?: string;
28
+ /** Member Skill names (i.e., directory names under `skills/`). */
29
+ skills: string[];
30
+ }
31
+ /** Grouping result: group metadata + member Skills read from library files. */
32
+ interface ResolvedSkillGroup extends Omit<SkillGroupInfo, "skills"> {
33
+ skills: LibrarySkill[];
34
+ }
35
+ /**
36
+ * Parses the frontmatter at the start of SKILL.md: only recognizes `key: value` lines inside the
37
+ * first `---` block (split on the first colon, value trimmed, values may themselves contain colons);
38
+ * all fields are scalars, no YAML dependency needed.
39
+ * Error tolerance: returns null if the `---` block or name is missing; version falls back to 1 if
40
+ * it isn't a natural number; updated defaults to "".
41
+ */
42
+ declare function parseSkillFrontmatter(content: string): SkillMetadata | null;
43
+ /** Reads all Skills in the library (one per subdirectory under `skills/`), sorted by name. */
44
+ declare function loadLibrarySkills(): LibrarySkill[];
45
+ /**
46
+ * Skill group manifest; members are library directory names.
47
+ * Docs: /docs/skills § "Built-in library".
48
+ */
49
+ declare const SKILL_GROUPS: SkillGroupInfo[];
50
+ /**
51
+ * Groups library Skills according to SKILL_GROUPS (a member name missing from `all` is skipped);
52
+ * Skills not listed in any group are appended to an Other group (only appears if non-empty). A
53
+ * pure function, the testable core of loadSkillGroups.
54
+ */
55
+ declare function groupSkills(all: LibrarySkill[]): ResolvedSkillGroup[];
56
+ /** Reads library files and groups them: SKILL_GROUPS order comes first, ungrouped Skills are appended to an Other group (only appears if non-empty). */
57
+ declare function loadSkillGroups(): ResolvedSkillGroup[];
58
+ /** Reads a single library Skill by name; returns undefined if the name contains illegal characters (path traversal guard) or doesn't exist. */
59
+ declare function librarySkill(name: string): LibrarySkill | undefined;
60
+
61
+ export { type LibrarySkill, type ResolvedSkillGroup, SKILL_GROUPS, type SkillGroupInfo, type SkillMetadata, groupSkills, librarySkill, loadLibrarySkills, loadSkillGroups, parseSkillFrontmatter };
package/dist/index.js ADDED
@@ -0,0 +1,133 @@
1
+ // src/index.ts
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import { fileURLToPath } from "url";
5
+ function parseSkillFrontmatter(content) {
6
+ const match = /^---\r?\n([\s\S]*?)\r?\n---/.exec(content.replace(/^\uFEFF/, ""));
7
+ if (!match) return null;
8
+ const fields = {};
9
+ for (const line of match[1].split(/\r?\n/)) {
10
+ const idx = line.indexOf(":");
11
+ if (idx <= 0) continue;
12
+ const key = line.slice(0, idx).trim();
13
+ if (key) fields[key] = line.slice(idx + 1).trim();
14
+ }
15
+ const name = fields["name"];
16
+ if (!name) return null;
17
+ const version = Number.parseInt(fields["version"] ?? "", 10);
18
+ const shortDescription = fields["short_description"];
19
+ const shortDescriptionZh = fields["short_description_zh"];
20
+ return {
21
+ name,
22
+ description: fields["description"] ?? "",
23
+ // short_description(_zh) is optional: omitted when absent (undefined keys aren't set).
24
+ ...shortDescription !== void 0 ? { shortDescription } : {},
25
+ ...shortDescriptionZh !== void 0 ? { shortDescriptionZh } : {},
26
+ version: Number.isInteger(version) && version >= 1 ? version : 1,
27
+ updated: fields["updated"] ?? ""
28
+ };
29
+ }
30
+ var SKILLS_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "skills");
31
+ var SKILL_NAME_PATTERN = /^[A-Za-z0-9_-]+$/;
32
+ function readSkillDir(name) {
33
+ const dir = path.join(SKILLS_ROOT, name);
34
+ let content;
35
+ try {
36
+ content = fs.readFileSync(path.join(dir, "SKILL.md"), "utf8");
37
+ } catch {
38
+ return void 0;
39
+ }
40
+ let icon;
41
+ try {
42
+ icon = fs.readFileSync(path.join(dir, "icon.svg"), "utf8");
43
+ } catch {
44
+ }
45
+ const meta = parseSkillFrontmatter(content) ?? {
46
+ name,
47
+ description: "",
48
+ version: 1,
49
+ updated: ""
50
+ };
51
+ return { ...meta, name, content, ...icon !== void 0 ? { icon } : {} };
52
+ }
53
+ function loadLibrarySkills() {
54
+ const skills = [];
55
+ for (const entry of fs.readdirSync(SKILLS_ROOT, { withFileTypes: true })) {
56
+ if (!entry.isDirectory()) continue;
57
+ const skill = readSkillDir(entry.name);
58
+ if (skill) skills.push(skill);
59
+ }
60
+ return skills.sort((a, b) => a.name.localeCompare(b.name));
61
+ }
62
+ var SKILL_GROUPS = [
63
+ {
64
+ id: "agent-development",
65
+ title: "Agent Development",
66
+ titleZh: "Agent \u5F00\u53D1",
67
+ skills: ["agent-creation", "benchmark-design", "agent-evaluation", "agent-optimization"]
68
+ },
69
+ {
70
+ id: "data-analysis",
71
+ title: "Data Analysis",
72
+ titleZh: "\u6570\u636E\u5206\u6790",
73
+ skills: ["data-analysis"]
74
+ },
75
+ {
76
+ id: "penguin-development",
77
+ title: "Penguin Development",
78
+ titleZh: "Penguin \u5F00\u53D1",
79
+ skills: ["penguin-sdk", "penguin-cli", "agenthub-models"]
80
+ },
81
+ {
82
+ id: "web-development",
83
+ title: "Web Development",
84
+ titleZh: "\u7F51\u9875\u5F00\u53D1",
85
+ skills: ["web-design"]
86
+ },
87
+ {
88
+ id: "software-engineering",
89
+ title: "Software Engineering",
90
+ titleZh: "\u8F6F\u4EF6\u5DE5\u7A0B",
91
+ skills: ["software-engineering"]
92
+ }
93
+ ];
94
+ function groupSkills(all) {
95
+ const byName = new Map(all.map((skill) => [skill.name, skill]));
96
+ const grouped = /* @__PURE__ */ new Set();
97
+ const groups = SKILL_GROUPS.map((group) => {
98
+ const members = [];
99
+ for (const name of group.skills) {
100
+ const skill = byName.get(name);
101
+ if (!skill) continue;
102
+ members.push(skill);
103
+ grouped.add(name);
104
+ }
105
+ return { ...group, skills: members };
106
+ });
107
+ const others = all.filter((skill) => !grouped.has(skill.name));
108
+ if (others.length > 0) {
109
+ groups.push({
110
+ id: "other",
111
+ title: "Other",
112
+ titleZh: "\u5176\u4ED6",
113
+ skills: others
114
+ });
115
+ }
116
+ return groups;
117
+ }
118
+ function loadSkillGroups() {
119
+ return groupSkills(loadLibrarySkills());
120
+ }
121
+ function librarySkill(name) {
122
+ if (!SKILL_NAME_PATTERN.test(name)) return void 0;
123
+ return readSkillDir(name);
124
+ }
125
+ export {
126
+ SKILL_GROUPS,
127
+ groupSkills,
128
+ librarySkill,
129
+ loadLibrarySkills,
130
+ loadSkillGroups,
131
+ parseSkillFrontmatter
132
+ };
133
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * PenguinHarness Skill library: built-in SKILL.md docs and skill group manifest.\n *\n * The runtime source of truth for library content is the package's `skills/<skill_name>/SKILL.md`\n * files: frontmatter is parsed on read (same rules as installed Skills), so editing a file takes\n * effect immediately with no caching (files are small, calls are infrequent). Only the skill group\n * manifest (id, title, and member names) is hardcoded in code; install / uninstall / scan still live\n * in core's state layer.\n *\n * Docs: packages/docs/content/skills.{zh,en}.md (site path /docs/skills) documents the Skill\n * format and the built-in library.\n */\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\n/** Skill's frontmatter metadata (four fields: name / description / version / updated; description itself is English-only, may also carry description_zh and short_description(_zh)). */\nexport interface SkillMetadata {\n /** Skill name (matches its containing directory name). */\n name: string;\n /** One-line description; injected into the model prompt via `{{SKILL_METADATA}}`. */\n description: string;\n /** UI short description (frontmatter `short_description`, optional): preferred in compact spots like cards, falls back to the full description if missing; not injected into the prompt. */\n shortDescription?: string;\n /** Chinese short description (frontmatter `short_description_zh`, optional). */\n shortDescriptionZh?: string;\n /** Version number (natural number); falls back to 1 on parse failure. */\n version: number;\n /** Update date (YYYY-MM-DD); defaults to \"\". */\n updated: string;\n}\n\n/** A Skill in the library: metadata + full SKILL.md content (including frontmatter, written as-is on install). */\nexport interface LibrarySkill extends SkillMetadata {\n content: string;\n /** Optional raw `icon.svg` content in the directory (custom icon, the file is the sole source, copied alongside SKILL.md on install); absent means none (frontend falls back to the default book icon). */\n icon?: string;\n}\n\n/** Skill group manifest entry: group id, title (optionally with a Chinese title, displayed per UI language), and member Skill names. */\nexport interface SkillGroupInfo {\n id: string;\n title: string;\n /** Chinese group title (optional, displayed per UI language). */\n titleZh?: string;\n /** Member Skill names (i.e., directory names under `skills/`). */\n skills: string[];\n}\n\n/** Grouping result: group metadata + member Skills read from library files. */\nexport interface ResolvedSkillGroup extends Omit<SkillGroupInfo, \"skills\"> {\n skills: LibrarySkill[];\n}\n\n/**\n * Parses the frontmatter at the start of SKILL.md: only recognizes `key: value` lines inside the\n * first `---` block (split on the first colon, value trimmed, values may themselves contain colons);\n * all fields are scalars, no YAML dependency needed.\n * Error tolerance: returns null if the `---` block or name is missing; version falls back to 1 if\n * it isn't a natural number; updated defaults to \"\".\n */\nexport function parseSkillFrontmatter(content: string): SkillMetadata | null {\n // Strip a possible UTF-8 BOM (may be introduced by editors when manually editing an installed SKILL.md); CRLF is handled by \\r?\\n.\n const match = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---/.exec(content.replace(/^\\uFEFF/, \"\"));\n if (!match) return null;\n const fields: Record<string, string> = {};\n for (const line of match[1]!.split(/\\r?\\n/)) {\n const idx = line.indexOf(\":\");\n if (idx <= 0) continue;\n const key = line.slice(0, idx).trim();\n if (key) fields[key] = line.slice(idx + 1).trim();\n }\n const name = fields[\"name\"];\n if (!name) return null;\n const version = Number.parseInt(fields[\"version\"] ?? \"\", 10);\n const shortDescription = fields[\"short_description\"];\n const shortDescriptionZh = fields[\"short_description_zh\"];\n return {\n name,\n description: fields[\"description\"] ?? \"\",\n // short_description(_zh) is optional: omitted when absent (undefined keys aren't set).\n ...(shortDescription !== undefined ? { shortDescription } : {}),\n ...(shortDescriptionZh !== undefined ? { shortDescriptionZh } : {}),\n version: Number.isInteger(version) && version >= 1 ? version : 1,\n updated: fields[\"updated\"] ?? \"\",\n };\n}\n\n/** Root directory of library files: the package's `skills/` (both dist/ and src/ sit one level below the package root, so one level up reaches it). */\nconst SKILLS_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), \"..\", \"skills\");\n\n/** Character rule for Skill names (directory names): prevents path traversal. */\nconst SKILL_NAME_PATTERN = /^[A-Za-z0-9_-]+$/;\n\n/**\n * Reads a single library directory to construct a LibrarySkill; returns undefined if SKILL.md\n * doesn't exist. name is taken from the directory name (overriding frontmatter); falls back to\n * empty metadata if frontmatter parsing fails.\n * The optional icon.svg in the directory is read alongside it (as a raw string); the icon field\n * is omitted if missing.\n */\nfunction readSkillDir(name: string): LibrarySkill | undefined {\n const dir = path.join(SKILLS_ROOT, name);\n let content: string;\n try {\n content = fs.readFileSync(path.join(dir, \"SKILL.md\"), \"utf8\");\n } catch {\n return undefined;\n }\n let icon: string | undefined;\n try {\n icon = fs.readFileSync(path.join(dir, \"icon.svg\"), \"utf8\");\n } catch {\n // icon.svg is optional: no custom icon if missing.\n }\n const meta = parseSkillFrontmatter(content) ?? {\n name,\n description: \"\",\n version: 1,\n updated: \"\",\n };\n return { ...meta, name, content, ...(icon !== undefined ? { icon } : {}) };\n}\n\n/** Reads all Skills in the library (one per subdirectory under `skills/`), sorted by name. */\nexport function loadLibrarySkills(): LibrarySkill[] {\n const skills: LibrarySkill[] = [];\n for (const entry of fs.readdirSync(SKILLS_ROOT, { withFileTypes: true })) {\n if (!entry.isDirectory()) continue;\n const skill = readSkillDir(entry.name);\n if (skill) skills.push(skill);\n }\n return skills.sort((a, b) => a.name.localeCompare(b.name));\n}\n\n/**\n * Skill group manifest; members are library directory names.\n * Docs: /docs/skills § \"Built-in library\".\n */\nexport const SKILL_GROUPS: SkillGroupInfo[] = [\n {\n id: \"agent-development\",\n title: \"Agent Development\",\n titleZh: \"Agent 开发\",\n skills: [\"agent-creation\", \"benchmark-design\", \"agent-evaluation\", \"agent-optimization\"],\n },\n {\n id: \"data-analysis\",\n title: \"Data Analysis\",\n titleZh: \"数据分析\",\n skills: [\"data-analysis\"],\n },\n {\n id: \"penguin-development\",\n title: \"Penguin Development\",\n titleZh: \"Penguin 开发\",\n skills: [\"penguin-sdk\", \"penguin-cli\", \"agenthub-models\"],\n },\n {\n id: \"web-development\",\n title: \"Web Development\",\n titleZh: \"网页开发\",\n skills: [\"web-design\"],\n },\n {\n id: \"software-engineering\",\n title: \"Software Engineering\",\n titleZh: \"软件工程\",\n skills: [\"software-engineering\"],\n },\n];\n\n/**\n * Groups library Skills according to SKILL_GROUPS (a member name missing from `all` is skipped);\n * Skills not listed in any group are appended to an Other group (only appears if non-empty). A\n * pure function, the testable core of loadSkillGroups.\n */\nexport function groupSkills(all: LibrarySkill[]): ResolvedSkillGroup[] {\n const byName = new Map(all.map((skill) => [skill.name, skill]));\n const grouped = new Set<string>();\n const groups: ResolvedSkillGroup[] = SKILL_GROUPS.map((group) => {\n const members: LibrarySkill[] = [];\n for (const name of group.skills) {\n const skill = byName.get(name);\n if (!skill) continue;\n members.push(skill);\n grouped.add(name);\n }\n return { ...group, skills: members };\n });\n const others = all.filter((skill) => !grouped.has(skill.name));\n if (others.length > 0) {\n groups.push({\n id: \"other\",\n title: \"Other\",\n titleZh: \"其他\",\n skills: others,\n });\n }\n return groups;\n}\n\n/** Reads library files and groups them: SKILL_GROUPS order comes first, ungrouped Skills are appended to an Other group (only appears if non-empty). */\nexport function loadSkillGroups(): ResolvedSkillGroup[] {\n return groupSkills(loadLibrarySkills());\n}\n\n/** Reads a single library Skill by name; returns undefined if the name contains illegal characters (path traversal guard) or doesn't exist. */\nexport function librarySkill(name: string): LibrarySkill | undefined {\n if (!SKILL_NAME_PATTERN.test(name)) return undefined;\n return readSkillDir(name);\n}\n"],"mappings":";AAYA,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,SAAS,qBAAqB;AA+CvB,SAAS,sBAAsB,SAAuC;AAE3E,QAAM,QAAQ,8BAA8B,KAAK,QAAQ,QAAQ,WAAW,EAAE,CAAC;AAC/E,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,SAAiC,CAAC;AACxC,aAAW,QAAQ,MAAM,CAAC,EAAG,MAAM,OAAO,GAAG;AAC3C,UAAM,MAAM,KAAK,QAAQ,GAAG;AAC5B,QAAI,OAAO,EAAG;AACd,UAAM,MAAM,KAAK,MAAM,GAAG,GAAG,EAAE,KAAK;AACpC,QAAI,IAAK,QAAO,GAAG,IAAI,KAAK,MAAM,MAAM,CAAC,EAAE,KAAK;AAAA,EAClD;AACA,QAAM,OAAO,OAAO,MAAM;AAC1B,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,UAAU,OAAO,SAAS,OAAO,SAAS,KAAK,IAAI,EAAE;AAC3D,QAAM,mBAAmB,OAAO,mBAAmB;AACnD,QAAM,qBAAqB,OAAO,sBAAsB;AACxD,SAAO;AAAA,IACL;AAAA,IACA,aAAa,OAAO,aAAa,KAAK;AAAA;AAAA,IAEtC,GAAI,qBAAqB,SAAY,EAAE,iBAAiB,IAAI,CAAC;AAAA,IAC7D,GAAI,uBAAuB,SAAY,EAAE,mBAAmB,IAAI,CAAC;AAAA,IACjE,SAAS,OAAO,UAAU,OAAO,KAAK,WAAW,IAAI,UAAU;AAAA,IAC/D,SAAS,OAAO,SAAS,KAAK;AAAA,EAChC;AACF;AAGA,IAAM,cAAc,KAAK,QAAQ,KAAK,QAAQ,cAAc,YAAY,GAAG,CAAC,GAAG,MAAM,QAAQ;AAG7F,IAAM,qBAAqB;AAS3B,SAAS,aAAa,MAAwC;AAC5D,QAAM,MAAM,KAAK,KAAK,aAAa,IAAI;AACvC,MAAI;AACJ,MAAI;AACF,cAAU,GAAG,aAAa,KAAK,KAAK,KAAK,UAAU,GAAG,MAAM;AAAA,EAC9D,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAI;AACJ,MAAI;AACF,WAAO,GAAG,aAAa,KAAK,KAAK,KAAK,UAAU,GAAG,MAAM;AAAA,EAC3D,QAAQ;AAAA,EAER;AACA,QAAM,OAAO,sBAAsB,OAAO,KAAK;AAAA,IAC7C;AAAA,IACA,aAAa;AAAA,IACb,SAAS;AAAA,IACT,SAAS;AAAA,EACX;AACA,SAAO,EAAE,GAAG,MAAM,MAAM,SAAS,GAAI,SAAS,SAAY,EAAE,KAAK,IAAI,CAAC,EAAG;AAC3E;AAGO,SAAS,oBAAoC;AAClD,QAAM,SAAyB,CAAC;AAChC,aAAW,SAAS,GAAG,YAAY,aAAa,EAAE,eAAe,KAAK,CAAC,GAAG;AACxE,QAAI,CAAC,MAAM,YAAY,EAAG;AAC1B,UAAM,QAAQ,aAAa,MAAM,IAAI;AACrC,QAAI,MAAO,QAAO,KAAK,KAAK;AAAA,EAC9B;AACA,SAAO,OAAO,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;AAC3D;AAMO,IAAM,eAAiC;AAAA,EAC5C;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,SAAS;AAAA,IACT,QAAQ,CAAC,kBAAkB,oBAAoB,oBAAoB,oBAAoB;AAAA,EACzF;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,SAAS;AAAA,IACT,QAAQ,CAAC,eAAe;AAAA,EAC1B;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,SAAS;AAAA,IACT,QAAQ,CAAC,eAAe,eAAe,iBAAiB;AAAA,EAC1D;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,SAAS;AAAA,IACT,QAAQ,CAAC,YAAY;AAAA,EACvB;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,SAAS;AAAA,IACT,QAAQ,CAAC,sBAAsB;AAAA,EACjC;AACF;AAOO,SAAS,YAAY,KAA2C;AACrE,QAAM,SAAS,IAAI,IAAI,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,MAAM,KAAK,CAAC,CAAC;AAC9D,QAAM,UAAU,oBAAI,IAAY;AAChC,QAAM,SAA+B,aAAa,IAAI,CAAC,UAAU;AAC/D,UAAM,UAA0B,CAAC;AACjC,eAAW,QAAQ,MAAM,QAAQ;AAC/B,YAAM,QAAQ,OAAO,IAAI,IAAI;AAC7B,UAAI,CAAC,MAAO;AACZ,cAAQ,KAAK,KAAK;AAClB,cAAQ,IAAI,IAAI;AAAA,IAClB;AACA,WAAO,EAAE,GAAG,OAAO,QAAQ,QAAQ;AAAA,EACrC,CAAC;AACD,QAAM,SAAS,IAAI,OAAO,CAAC,UAAU,CAAC,QAAQ,IAAI,MAAM,IAAI,CAAC;AAC7D,MAAI,OAAO,SAAS,GAAG;AACrB,WAAO,KAAK;AAAA,MACV,IAAI;AAAA,MACJ,OAAO;AAAA,MACP,SAAS;AAAA,MACT,QAAQ;AAAA,IACV,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAGO,SAAS,kBAAwC;AACtD,SAAO,YAAY,kBAAkB,CAAC;AACxC;AAGO,SAAS,aAAa,MAAwC;AACnE,MAAI,CAAC,mBAAmB,KAAK,IAAI,EAAG,QAAO;AAC3C,SAAO,aAAa,IAAI;AAC1B;","names":[]}
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@prismshadow/penguin-skills",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "description": "PenguinHarness skill library: built-in SKILL.md documents and skill groups, decoupled from core.",
6
+ "license": "Apache-2.0",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/Prism-Shadow/penguin-harness.git",
10
+ "directory": "packages/skills"
11
+ },
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "import": "./dist/index.js"
16
+ }
17
+ },
18
+ "main": "./dist/index.js",
19
+ "types": "./dist/index.d.ts",
20
+ "devDependencies": {
21
+ "@types/node": "^24.0.0",
22
+ "tsup": "^8.3.0",
23
+ "typescript": "^5.6.0",
24
+ "vitest": "^2.1.0"
25
+ },
26
+ "files": [
27
+ "dist",
28
+ "skills",
29
+ "LICENSE"
30
+ ],
31
+ "publishConfig": {
32
+ "access": "public"
33
+ },
34
+ "scripts": {
35
+ "typecheck": "tsc --noEmit -p tsconfig.json",
36
+ "test": "vitest run --passWithNoTests",
37
+ "build": "tsup"
38
+ }
39
+ }
@@ -0,0 +1,73 @@
1
+ ---
2
+ name: agent-creation
3
+ description: Turn a user requirement into a concrete agent — write the target agent's AGENTS.md and install the skills it needs.
4
+ short_description: Turn a requirement into a working agent.
5
+ short_description_zh: 把需求变成可用的 Agent。
6
+ version: 1
7
+ updated: 2026-07-17T00:00:00Z
8
+ ---
9
+
10
+ # Agent Creation
11
+
12
+ This skill turns a user requirement into a working agent configuration — plain files in the target agent's directory.
13
+
14
+ ## Before you start
15
+
16
+ If the user's message only invokes this skill (e.g. "use agent-creation skill") without a concrete requirement, ask the user what agent they want and what it should do. Do not start until the requirement is clear.
17
+
18
+ ## Locate the target agent
19
+
20
+ All agents of this project live side by side in the project directory:
21
+
22
+ ```bash
23
+ PROJECT_DIR="<project_dir>" # the Project Dir value from your Environment section
24
+ ls "$PROJECT_DIR/agents" # existing agents (each is a folder here)
25
+ TARGET="$PROJECT_DIR/agents/<agent_id>" # the agent to configure
26
+ ```
27
+
28
+ An agent directory contains `agent_state/` (`system_config.yaml`, `AGENTS.md`, `skills/`, `memory/`, `tools/`) plus `scratchpad/` — and `traces/`, which appears once the agent has run at least once.
29
+
30
+ ## Write AGENTS.md
31
+
32
+ `agent_state/AGENTS.md` is injected into the agent's system prompt — it is where the user requirement becomes behavior. Keep `system_config.yaml`'s `system_prompt` untouched (that is the stable system layer); put everything requirement-specific in AGENTS.md:
33
+
34
+ - Role — what the agent is for, in one or two sentences.
35
+ - Domain guidance — the concrete rules, steps and constraints derived from the user requirement.
36
+
37
+ Be concise: AGENTS.md is prompt context, not documentation.
38
+
39
+ ## Install skills
40
+
41
+ A skill is a directory `agent_state/skills/<skill_name>/` containing a `SKILL.md`:
42
+
43
+ ```md
44
+ ---
45
+ name: <skill_name>
46
+ description: <skill_description>
47
+ version: 1
48
+ updated: <ISO 8601 timestamp>
49
+ ---
50
+
51
+ <skill_instructions>
52
+ ```
53
+
54
+ The frontmatter may also carry optional `short_description` and `short_description_zh` lines (a short UI blurb and its Chinese variant) — the UI prefers them for display, while prompt injection always uses the English `description`.
55
+
56
+ Installing is all it takes: the frontmatter metadata of every `SKILL.md` under `skills/` is injected into the target agent's system prompt automatically — do not register skills in AGENTS.md.
57
+
58
+ Write skills yourself, or fetch existing ones from the internet with shell commands (`curl`, `git clone`) and place them under `skills/`. Anything fetched from the internet must be read in full and reviewed before installing — a skill becomes durable instructions the target agent will follow in every future session; never install one you have not read, and tell the user what it does.
59
+
60
+ ## Set name and description
61
+
62
+ In the target's `agent_state/system_config.yaml`, set the top-level `name:` and `description:` fields so the agent is recognizable in lists. Edit only these two fields.
63
+
64
+ ## Creating a brand-new agent
65
+
66
+ Prefer configuring an agent the user already created. If you must create one from scratch: pick a short id (letters, digits, `_`, `-`), copy the default agent's `system_config.yaml` as the base, and create the layout described above:
67
+
68
+ ```bash
69
+ mkdir -p "$TARGET/agent_state/skills" "$TARGET/agent_state/memory" "$TARGET/agent_state/tools" "$TARGET/scratchpad"
70
+ cp "$PROJECT_DIR/agents/default_agent/agent_state/system_config.yaml" "$TARGET/agent_state/"
71
+ ```
72
+
73
+ A new agent starts with no skills — install only what it needs. Then write its AGENTS.md, name and description as above.
@@ -0,0 +1,6 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">
2
+ <circle cx="9.5" cy="8" r="3.3" />
3
+ <path d="M3.8 20c0-3.1 2.6-5.7 5.7-5.7 1.5 0 2.9.6 3.9 1.5" />
4
+ <path d="M18 13.5v7" />
5
+ <path d="M14.5 17h7" />
6
+ </svg>