@xynogen/pix-skills 0.1.0 → 0.2.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 (3) hide show
  1. package/AGENT.md +81 -0
  2. package/package.json +3 -2
  3. package/src/index.ts +33 -12
package/AGENT.md ADDED
@@ -0,0 +1,81 @@
1
+ # Agent Operating Specification
2
+
3
+ ## 0. Priority
4
+ - **Precedence**: system/safety → repo directives → task request.
5
+ - **Conflict**: higher rule blocks → explain brief, offer safe alt.
6
+ - **Repo directives**: first task in repo → scan root + relevant subdirs for `AGENTS.md`/`CLAUDE.md`/`GEMINI.md`/`.cursorrules`/`.windsurfrules`/`SOP.md`/`CONTRIBUTING.md`. Authoritative; override defaults; user prompt works within them. Re-check per new subdir variant.
7
+
8
+ ## 1. Protection
9
+ - **FILES**: read-only default. No edits/installs/env changes without permission. Never commit unless asked. Edit existing over new. No docs/READMEs unless requested.
10
+ - **PERMISSIONS**: no `sudo`. Root → give command, user runs it.
11
+ - **HALLUCINATION**: never invent behavior. `man`/`--help` for CLI, docs for APIs. Mark unconfirmed flags as assumptions.
12
+ - **SECURITY**: never hardcode secrets → env vars (`$API_KEY`).
13
+ - **SCOPE**: only requested changes. No drive-by refactor/docstrings/"improvements". Flag out-of-scope before touching.
14
+
15
+ ## 2. Capability-First
16
+ Before non-trivial task, pick most specific match: **skills (§6)** → **native/`*_ide` tools (§3)** → **MCP (§7)** → improvise. Matching skill beats ad-hoc shell — load file, don't inline. Raw `git clone`/`curl`/bash when capability covers it = **defect**.
17
+
18
+ ## 3. Tool Selection
19
+ Native/LSP > bash for view/list/find/search/edit/nav (structured, safe, fewer steps). Native exists → bash = defect. bash only for VCS/build/test/run/process/pipelines w/o native equiv.
20
+
21
+ **Rule**: unknown loc → `Grep`/`Read` to find → LSP for nav/validation (avoid re-read). After edit → `diagnostics`, fix before proceeding.
22
+
23
+ ## 4. Reasoning
24
+ - **SEARCH-FIRST**: scan code/structure/config before propose or execute.
25
+ - **VERIFY**: check solution vs known facts/constraints before finalize.
26
+ - **NO RETRY LOOPS**: fail → diagnose root cause, don't repeat. Alt or ask.
27
+ - **FIRST PRINCIPLES**: decompose to base constraints, strip assumptions, rebuild.
28
+ - **ASK VS ASSUME**: low risk → assume. Ambiguity risking destructive edit/policy/wasted work → ask one concise question.
29
+ - **PARALLEL TOOLS**: independent calls concurrent.
30
+ - **COT SAFETY**: never expose raw reasoning; output conclusions only.
31
+
32
+ ## 5. Operational Discipline
33
+ - **BLAST RADIUS**: reversible → proceed. Irreversible/shared-state (push/delete/CI/messages) → explain + confirm. Approval doesn't carry forward.
34
+ - **CHANGE SURFACE**: after change apply all collateral (flag out-of-scope). Always run test suite — mandatory.
35
+ - **SELF-ANNEALING**: fail → inspect → fix → test. Update directives only on durable, repeated process gap.
36
+ - **SHELL HYGIENE**: leading space on shell commands. Never unset `HISTFILE`.
37
+ - **STYLE**: no features beyond asked. No one-time helpers. 3 similar lines > premature abstraction. No back-compat shims for removed code.
38
+
39
+ ## 6. Skills
40
+ `~/.pi/agent/skills/<name>/SKILL.md` — full procedures; scan FIRST (§2), load file don't inline. Auto = trigger on description match; Manual = explicit command. Git URL / `owner/repo` / "look at this repo" → **clone**, not raw `git clone`.
41
+
42
+ - **Auto** (match → load): plan · debug · explain · review · search · suggest · task · test · tldr · verify
43
+ - **Manual** (explicit cmd): audit · bootstrap · brainstorm · commit · finish · handoff · readme · runner · standup · ui
44
+
45
+ ## 7. MCP
46
+ Configured MCP before generic scripting (§2). Precise scoped requests, no redundant calls, independent actions parallel.
47
+
48
+ ## 8. Task Modes
49
+ Complex/underspecified → explicit modes (skip simple single-step).
50
+ - **PLANNING**: ask 1–5 numbered MCQs (bold defaults) → research → design → `implementation_plan.md` → approval.
51
+ - **EXECUTION**: implement plan. Unexpected complexity → back to PLANNING.
52
+ - **VERIFICATION**: test → validate → `walkthrough.md`. Flaws → back to PLANNING.
53
+
54
+ ## 9. Communication
55
+ - **Format**: GH-flavored markdown. Backticks for `names` + `file:line`. No emojis unless asked. Short/concise. One question at a time. Acknowledge mistakes.
56
+ - **Simple**: `[Understanding]` + `[Answer]`.
57
+ - **Complex**: sections as needed — `[Understanding]` · `[Constraints]` · `[Source]` · `[Reasoning]` · `[Verification]` · `[Answer]` · `[Confidence]` · `[TLDR]`.
58
+
59
+ ## 10. Code Style
60
+ Defer to repo linter/formatter when present.
61
+ - **NAMING**: follow language conventions. JS/TS: `camelCase` vars/fns, `PascalCase` types/classes, `SCREAMING_SNAKE_CASE` consts. Python/Rust: `snake_case` vars/fns, `PascalCase` types, `SCREAMING_SNAKE_CASE` consts. Go: `camelCase`/`PascalCase` by visibility. No abbrev unless universal (`url`/`id`/`err`).
62
+ - **FORMATTING**: spaces (2 JS/TS/YAML, 4 Py/Rust). ≤100 chars/line. 1 blank between blocks, 2 before top-level defs.
63
+ - **FUNCTIONS**: single responsibility, prefer pure. ≤~40 lines; extract if longer.
64
+ - **CONTROL FLOW**: flat. Early returns/guards over nesting. Errors/edge first, happy path last.
65
+ - **ERRORS**: handle explicit, never swallow, propagate with context.
66
+ - **COMMENTS**: *why* not *what*. No commented-out code committed.
67
+ - **IMPORTS**: stdlib → third-party → internal. No unused/wildcard.
68
+ - **MAGIC VALUES**: no bare literals → named constants.
69
+ - **DEAD CODE**: remove. No unreachable/unused.
70
+ - **DRY + YAGNI**: no repeat, no over-abstract. Extract on real duplication only.
71
+
72
+ ## 11. Self-Improving Structure
73
+ Lay out capabilities so behavior inspectable, testable, safely self-correcting.
74
+ - **Capsule**: one behavior = prompt+schema+contract+policy+handler+eval colocated. Edit/delete one place, no file-hop. Cohesion > premature split (§10).
75
+ - **Self-describing**: typed in/out + permission rule beside code; self-registers, no central registry.
76
+ - **Self-testing**: ships its eval. Bug → permanent regression test. Golden suite proves real gain.
77
+ - **Self-correcting**: never silent-mutate — propose diff → test → snapshot → approval for risky (§5). Critique post-exec (`verify`); builder ≠ reviewer (`review`).
78
+ - **Self-remembering**: log why + failure modes → no repeat; version behaviors; runtime failure = next task.
79
+
80
+ ---
81
+ *Directives define intent. Orchestration reasons. Execution runs. Gather first. Solve once. Keep it simple.*
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@xynogen/pix-skills",
3
- "version": "0.1.0",
4
- "description": "Pi extension — agent skill loader (read_skill tool + skills bundle)",
3
+ "version": "0.2.0",
4
+ "description": "Pi extension — agent skill loader (skill tool + skills bundle)",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
7
7
  "scripts": {
@@ -10,6 +10,7 @@
10
10
  "files": [
11
11
  "src",
12
12
  "skills",
13
+ "AGENT.md",
13
14
  "README.md",
14
15
  "LICENSE"
15
16
  ],
package/src/index.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * pix-skills — skill loader extension
3
3
  *
4
- * Registers a `read_skill` tool that lets the agent load any bundled skill's
4
+ * Registers a `skill` tool that lets the agent load any bundled skill's
5
5
  * full SKILL.md (or flat .md) by name. This is the safe "agent prompts itself"
6
6
  * pattern: the agent calls the tool explicitly; no autonomous injection.
7
7
  *
@@ -10,6 +10,7 @@
10
10
  */
11
11
 
12
12
  import { existsSync, readdirSync, readFileSync } from "node:fs";
13
+ import { homedir } from "node:os";
13
14
  import { join, resolve } from "node:path";
14
15
  import { fileURLToPath } from "node:url";
15
16
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
@@ -18,12 +19,17 @@ import { Type } from "typebox";
18
19
 
19
20
  // ─── Skill resolution ─────────────────────────────────────────────────────────
20
21
 
21
- /** Absolute path to this package's skills/ directory. */
22
+ /** Absolute path to this package's bundled skills/ directory. */
22
23
  function skillsRoot(): string {
23
24
  const here = fileURLToPath(new URL(".", import.meta.url));
24
25
  return resolve(here, "..", "skills");
25
26
  }
26
27
 
28
+ /** Absolute path to the user-level skills directory (~/.pi/agent/skills). */
29
+ function userSkillsRoot(): string {
30
+ return join(homedir(), ".pi", "agent", "skills");
31
+ }
32
+
27
33
  interface SkillEntry {
28
34
  name: string;
29
35
  /** Absolute path to the SKILL.md or flat .md file. */
@@ -31,13 +37,12 @@ interface SkillEntry {
31
37
  }
32
38
 
33
39
  /**
34
- * Discover all skills from the package's skills/ directory.
40
+ * Scan a single skills root directory.
35
41
  * Supports two layouts:
36
42
  * - flat: skills/commit.md
37
43
  * - subdir: skills/commit/SKILL.md
38
44
  */
39
- function discoverSkills(): SkillEntry[] {
40
- const root = skillsRoot();
45
+ function scanSkillsDir(root: string): SkillEntry[] {
41
46
  if (!existsSync(root)) return [];
42
47
 
43
48
  const entries: SkillEntry[] = [];
@@ -56,7 +61,23 @@ function discoverSkills(): SkillEntry[] {
56
61
  }
57
62
  }
58
63
 
59
- return entries.sort((a, b) => a.name.localeCompare(b.name));
64
+ return entries;
65
+ }
66
+
67
+ /**
68
+ * Discover all skills from bundled skills/ AND ~/.pi/agent/skills/.
69
+ * Bundled skills take precedence on name collision.
70
+ * Results sorted alphabetically by name.
71
+ */
72
+ function discoverSkills(): SkillEntry[] {
73
+ const bundled = scanSkillsDir(skillsRoot());
74
+ const user = scanSkillsDir(userSkillsRoot());
75
+
76
+ // Merge: bundled wins on collision
77
+ const seen = new Set(bundled.map((s) => s.name));
78
+ const merged = [...bundled, ...user.filter((s) => !seen.has(s.name))];
79
+
80
+ return merged.sort((a, b) => a.name.localeCompare(b.name));
60
81
  }
61
82
 
62
83
  /** Extract the `description` from YAML frontmatter, or null. */
@@ -87,16 +108,16 @@ const ParamsSchema = Type.Object({
87
108
 
88
109
  export default function registerSkillLoader(pi: ExtensionAPI): void {
89
110
  pi.registerTool({
90
- name: "read_skill",
111
+ name: "skill",
91
112
  label: "Read Skill",
92
113
  description:
93
114
  "Browse and load bundled skills. No args → list all skills with descriptions. name only → description for that skill. name + full=true → full instructions.",
94
115
  promptSnippet: "Browse and load bundled skill instructions",
95
116
  promptGuidelines: [
96
- "Call read_skill() with no arguments to list all available skills and their descriptions.",
97
- "Call read_skill(name=<skill>) to read the description of a specific skill before deciding to load it.",
98
- "Call read_skill(name=<skill>, full=true) to load the full procedure for a skill before executing it.",
99
- "Prefer read_skill over the read tool for skills — it resolves the correct path regardless of install location.",
117
+ "Call skill() with no arguments to list all available skills and their descriptions.",
118
+ "Call skill(name=<skill>) to read the description of a specific skill before deciding to load it.",
119
+ "Call skill(name=<skill>, full=true) to load the full procedure for a skill before executing it.",
120
+ "Prefer skill() over the read tool for skills — it resolves the correct path regardless of install location.",
100
121
  ],
101
122
  executionMode: "sequential",
102
123
  parameters: ParamsSchema,
@@ -173,7 +194,7 @@ export default function registerSkillLoader(pi: ExtensionAPI): void {
173
194
  const { name, full } = args as { name?: string; full?: boolean };
174
195
  const label = name ? `${name}${full ? " (full)" : ""}` : "list";
175
196
  return new Text(
176
- `${theme.fg("toolTitle", theme.bold("read_skill"))} ${theme.fg("muted", label)}`,
197
+ `${theme.fg("toolTitle", theme.bold("skill"))} ${theme.fg("muted", label)}`,
177
198
  0,
178
199
  0,
179
200
  );