refactor(skills): relocate skill library to src/aipass/skills/lib/ — rename catalog/, move telegram in, archive orphan fixtures, retire .aipass/skills/

Unifies all 6 first-party skills under lib/ (built-in tier). Fixes telegram not
being cross-branch discoverable (was in cwd-relative .aipass/skills/). Built-in
discovery path catalog->lib; telegram conftest parents[6]->[5]; .service
ExecStart, seedgo bypass + test paths updated. Packaging/imports/gitignore
unaffected (stays under src/aipass). 252/252 tests green, cross-branch discovery
verified. DPLAN-0218.
This commit is contained in:
AIOSAI
2026-06-24 14:24:28 -07:00
parent 57767cc2a9
commit 882da7cdfc
66 changed files with 55 additions and 205 deletions
+18
View File
@@ -11,6 +11,24 @@ PyPI version — not the changelog header.
## [2026-06-24]
### Changed
- **Skill library relocated to `src/aipass/skills/lib/`** — first-party skills
were split across `catalog/` (built-in, cross-branch) and `.aipass/skills/`
(the branch-prompt dir, cwd-relative). Renamed `catalog/`→`lib/`, moved the
telegram skill in, archived three orphan test-fixture skills, and retired
`.aipass/skills/` from the branch. This unifies all 6 first-party skills under
one built-in tier and **fixes the telegram skill not being discoverable from
other branches** (it sat in a cwd-relative path). The public discovery
convention (`.aipass/skills/` + `~/.aipass/skills/`) is unchanged. One
functional line changed (`discovery_handler` built-in path); telegram's test
`conftest` path-depth, the systemd `.service` ExecStart, and seedgo bypass +
test paths were updated to match. Packaging, imports, and gitignore are
unaffected (everything stays under `src/aipass/`). 252/252 skills tests green;
cross-branch discovery verified from another branch. Moving telegram into the
gate's scope newly surfaced 9 pre-existing `unused_function` flags in its
handlers — triage tracked separately. (DPLAN-0218)
### Fixed
- **seedgo CLI help checkers green-lit non-compliant `--help` output** — the
@@ -1,5 +1,5 @@
# SKILLS — Branch Context
<!-- File: src/skills/.aipass/aipass_local_prompt.md — Injected on every prompt when in skills directory. -->
<!-- File: src/aipass/skills/.aipass/aipass_local_prompt.md — Injected on every prompt when in skills directory. -->
Capability framework for AI agents. Discoverable, validatable, executable skill units across three tiers: markdown-only, with handler, full 3-layer.
@@ -34,7 +34,7 @@ apps/
│ ├── validator.py # Requirement checking (pip, bins, config)
│ └── template.py # Template resolution and copying
├── plugins/ # Extension point (empty)
catalog/ # Built-in skills: drone_commands, github, system_status
lib/ # Built-in skills: branch_health, drone_commands, github, inbox_check, system_status, telegram
templates/ # Skill creation templates (markdown_only, with_handler, full)
```
@@ -42,7 +42,7 @@ templates/ # Skill creation templates (markdown_only, with_handl
1. `.aipass/skills/` — Project-local skills
2. `~/.aipass/skills/` — Global user skills
3. `src/skills/catalog/` — Built-in skills
3. `src/aipass/skills/lib/` — Built-in skills
## Three Skill Tiers
@@ -1,27 +0,0 @@
---
name: another_test
description: TODO — describe what this skill does
version: 1.0.0
tags: []
requires:
pip: []
bins: []
config: []
has_handler: true
---
# another_test
## What This Does
TODO
## When to Use
TODO
## Steps
1. TODO
## Example
```
TODO
```
@@ -1,30 +0,0 @@
"""
another_test skill handler
Called by: drone @skills run another_test <action> [args]
"""
def run(action, args=None, config=None):
"""Execute a skill action.
Args:
action: What to do
args: Dict of action arguments
config: Dict of resolved config values
Returns:
{"success": bool, "output": str, "error": str|None}
"""
args = args or {}
config = config or {}
if action == "example":
return {"success": True, "output": "It works!", "error": None}
return {"success": False, "output": "", "error": f"Unknown action: {action}"}
def get_actions():
"""List available actions for this skill."""
return ["example"]
@@ -1,27 +0,0 @@
---
name: full_test
description: TODO — describe what this skill does
version: 1.0.0
tags: []
requires:
pip: []
bins: []
config: []
has_handler: true
---
# full_test
## What This Does
TODO
## When to Use
TODO
## Steps
1. TODO
## Example
```
TODO
```
@@ -1,7 +0,0 @@
# ===================AIPASS====================
# META DATA HEADER
# Name: __init__.py - full_test apps package
# Date: 2026-03-07
# Version: 1.0.0
# Category: skills/catalog/full_test/apps
# =============================================
@@ -1,13 +0,0 @@
# ===================AIPASS====================
# META DATA HEADER
# Name: __init__.py - full_test handlers package
# Date: 2026-03-07
# Version: 1.0.0
# Category: skills/catalog/full_test/apps/handlers
#
# CHANGELOG (Max 5 entries):
# - v1.0.0 (2026-03-07): Initial scaffold
#
# CODE STANDARDS:
# - Handlers layer: returns dicts, NEVER prints
# =============================================
@@ -1,13 +0,0 @@
# ===================AIPASS====================
# META DATA HEADER
# Name: __init__.py - full_test modules package
# Date: 2026-03-07
# Version: 1.0.0
# Category: skills/catalog/full_test/apps/modules
#
# CHANGELOG (Max 5 entries):
# - v1.0.0 (2026-03-07): Initial scaffold
#
# CODE STANDARDS:
# - Modules layer: orchestration (can print)
# =============================================
@@ -1,24 +0,0 @@
"""
full_test — Full 3-layer skill handler.
Scaffolded by: drone @skills create full_test --full
"""
def run(action: str, args: list, config: dict) -> dict:
"""
Execute the skill.
Args:
action: The action to perform
args: Command arguments
config: Skill configuration from SKILL.md
Returns:
dict with keys: success (bool), output (str), error (str|None)
"""
return {
"success": True,
"output": f"full_test executed action: {action}",
"error": None,
}
@@ -1,27 +0,0 @@
---
name: test_skill
description: TODO — describe what this skill does
version: 1.0.0
tags: []
requires:
pip: []
bins: []
config: []
has_handler: false
---
# test_skill
## What This Does
TODO
## When to Use
TODO
## Steps
1. TODO
## Example
```
TODO
```
+2 -2
View File
@@ -6,12 +6,12 @@
},
"bypass": [
{
"file": ".aipass/skills/telegram/tests/test_response_router.py",
"file": "lib/telegram/tests/test_response_router.py",
"standard": "architecture",
"reason": "Test file — lives in tests/ by convention, not in the 3-layer app structure. Test files are exempt from layer architecture standard."
},
{
"file": ".aipass/skills/telegram/tests/test_response_router.py",
"file": "lib/telegram/tests/test_response_router.py",
"standard": "encapsulation",
"reason": "Test file — imports handler module directly for unit testing. Tests need direct access to monkeypatch module-level attributes and verify handler behavior."
},
+3 -3
View File
@@ -104,7 +104,7 @@ Skills are discovered in this order (first match wins for same name):
1. **Project**: `.aipass/skills/` in the current working directory
2. **Global**: `~/.aipass/skills/` in the user's home directory
3. **Built-in**: `src/skills/catalog/` in the AIPass codebase
3. **Built-in**: `src/aipass/skills/lib/` in the AIPass codebase
## Commands / Usage
@@ -124,7 +124,7 @@ drone @skills --help # Show help
## Directory Structure
```
src/skills/
src/aipass/skills/
apps/
skills.py # Entry point (handle_command)
modules/
@@ -140,7 +140,7 @@ src/skills/
validator.py # Check requirements
template.py # Skill templates
plugins/ # Plugin extensions
catalog/ # Built-in skills (branch_health, drone_commands, github, inbox_check, system_status)
lib/ # Built-in skills (branch_health, drone_commands, github, inbox_check, system_status, telegram)
templates/ # Skill creation templates
skills_json/ # JSON tracking directory
dropbox/ # External storage sync
@@ -39,7 +39,7 @@ def get_search_paths():
Search order (first match wins for same name):
1. Current project: .aipass/skills/
2. Global user: ~/.aipass/skills/
3. Built-in: src/skills/catalog/
3. Built-in: src/aipass/skills/lib/
Returns:
list[tuple[Path, str]]: List of (path, source_label) tuples.
@@ -54,8 +54,8 @@ def get_search_paths():
global_path = Path.home() / ".aipass" / "skills"
paths.append((global_path, "global"))
# 3. Built-in catalog
builtin_path = Path(__file__).resolve().parent.parent.parent / "catalog"
# 3. Built-in lib
builtin_path = Path(__file__).resolve().parent.parent.parent / "lib"
paths.append((builtin_path, "builtin"))
return paths
+1 -1
View File
@@ -13,7 +13,7 @@ from aipass.prax import logger
from aipass.skills.apps.handlers.json import json_handler
# Template directory lives at src/skills/templates/
# Template directory lives at src/aipass/skills/templates/
TEMPLATES_DIR = Path(__file__).resolve().parent.parent.parent / "templates"
VALID_TYPES = ("markdown_only", "with_handler", "full")
+1 -1
View File
@@ -123,7 +123,7 @@ def print_help():
console.print("Search paths (first match wins):")
console.print(" 1. .aipass/skills/ Project-local skills")
console.print(" 2. ~/.aipass/skills/ Global user skills")
console.print(" 3. src/skills/catalog/ Built-in skills")
console.print(" 3. src/aipass/skills/lib/ Built-in skills")
def _cmd_list():
@@ -3,7 +3,7 @@
# Name: handler.py - Branch Health skill handler
# Date: 2026-03-29
# Version: 1.0.0
# Category: skills/catalog/branch_health
# Category: skills/lib/branch_health
# =============================================
"""
@@ -58,7 +58,7 @@ def get_actions():
def _src_root():
"""Return the src/ directory by navigating up from this handler."""
# handler.py -> branch_health/ -> catalog/ -> skills/ -> aipass/ -> src/
# handler.py -> branch_health/ -> lib/ -> skills/ -> aipass/ -> src/
return Path(__file__).resolve().parents[4]
@@ -3,7 +3,7 @@
# Name: __init__.py - drone_commands apps package
# Date: 2026-03-07
# Version: 1.0.0
# Category: skills/catalog/drone_commands/apps
# Category: skills/lib/drone_commands/apps
#
# CHANGELOG (Max 5 entries):
# - v1.0.0 (2026-03-07): Initial implementation
@@ -3,7 +3,7 @@
# Name: __init__.py - drone_commands handlers package
# Date: 2026-03-07
# Version: 1.0.0
# Category: skills/catalog/drone_commands/apps/handlers
# Category: skills/lib/drone_commands/apps/handlers
#
# CHANGELOG (Max 5 entries):
# - v1.0.0 (2026-03-07): Initial implementation
@@ -3,7 +3,7 @@
# Name: executor.py - Runs drone commands via subprocess
# Date: 2026-03-07
# Version: 1.0.0
# Category: skills/catalog/drone_commands/apps/handlers
# Category: skills/lib/drone_commands/apps/handlers
#
# CHANGELOG (Max 5 entries):
# - v1.0.0 (2026-03-07): Initial implementation
@@ -3,7 +3,7 @@
# Name: parser.py - Parses drone command output
# Date: 2026-03-07
# Version: 1.0.0
# Category: skills/catalog/drone_commands/apps/handlers
# Category: skills/lib/drone_commands/apps/handlers
#
# CHANGELOG (Max 5 entries):
# - v1.0.0 (2026-03-07): Initial implementation
@@ -3,7 +3,7 @@
# Name: __init__.py - drone_commands modules package
# Date: 2026-03-07
# Version: 1.0.0
# Category: skills/catalog/drone_commands/apps/modules
# Category: skills/lib/drone_commands/apps/modules
#
# CHANGELOG (Max 5 entries):
# - v1.0.0 (2026-03-07): Initial implementation
@@ -3,7 +3,7 @@
# Name: command_runner.py - Orchestrates drone command execution
# Date: 2026-03-07
# Version: 1.0.0
# Category: skills/catalog/drone_commands/apps/modules
# Category: skills/lib/drone_commands/apps/modules
#
# CHANGELOG (Max 5 entries):
# - v1.0.0 (2026-03-07): Initial implementation
@@ -3,7 +3,7 @@
# Name: handler.py - Drone Commands skill handler
# Date: 2026-03-07
# Version: 1.0.0
# Category: skills/catalog/drone_commands
# Category: skills/lib/drone_commands
#
# CHANGELOG (Max 5 entries):
# - v1.0.0 (2026-03-07): Initial implementation
@@ -3,7 +3,7 @@
# Name: handler.py - Inbox Check skill handler
# Date: 2026-03-29
# Version: 1.0.0
# Category: skills/catalog/inbox_check
# Category: skills/lib/inbox_check
# =============================================
"""
@@ -57,7 +57,7 @@ def get_actions():
def _src_root():
"""Return the src/ directory by navigating up from this handler."""
# handler.py -> inbox_check/ -> catalog/ -> skills/ -> aipass/ -> src/
# handler.py -> inbox_check/ -> lib/ -> skills/ -> aipass/ -> src/
return Path(__file__).resolve().parents[4]
@@ -3,7 +3,7 @@
# Name: handler.py - System Status skill handler
# Date: 2026-03-07
# Version: 1.0.0
# Category: skills/catalog/system_status
# Category: skills/lib/system_status
# =============================================
"""
@@ -3,5 +3,5 @@
# Name: __init__.py - telegram apps package
# Date: 2026-03-07
# Version: 1.0.0
# Category: skills/catalog/telegram/apps
# Category: skills/lib/telegram/apps
# =============================================
@@ -3,7 +3,7 @@
# Name: bot_factory.py - Bot creation and deletion factory
# Date: 2026-06-15
# Version: 1.0.0
# Category: skills/catalog/telegram/apps/handlers
# Category: skills/lib/telegram/apps/handlers
#
# CHANGELOG (Max 5 entries):
# - v1.0.0 (2026-06-15): Ported from Dev-Pass — rewired registry, logger, config, base_bot path
@@ -3,7 +3,7 @@
# Name: __init__.py - telegram modules package
# Date: 2026-03-07
# Version: 1.0.0
# Category: skills/catalog/telegram/apps/modules
# Category: skills/lib/telegram/apps/modules
#
# CHANGELOG (Max 5 entries):
# - v1.0.0 (2026-03-07): Initial scaffold
@@ -19,7 +19,7 @@ Wants=network-online.target
[Service]
Type=simple
ExecStart=%h/.venv/bin/python3 %h/Projects/AIPass/src/aipass/skills/.aipass/skills/telegram/apps/handlers/base_bot.py --bot-id %i
ExecStart=%h/.venv/bin/python3 %h/Projects/AIPass/src/aipass/skills/lib/telegram/apps/handlers/base_bot.py --bot-id %i
WorkingDirectory=%h/Projects/AIPass
Environment=AIPASS_BOT_ID=%i
Environment=AIPASS_SESSION_TYPE=telegram
@@ -28,7 +28,7 @@ if "AIPASS_TEST_LOG_DIR" not in os.environ:
import pytest
# Add src/ to path so aipass.* is importable
_src_root = Path(__file__).resolve().parents[6] # noqa: E402
_src_root = Path(__file__).resolve().parents[5] # noqa: E402
if str(_src_root) not in sys.path:
sys.path.insert(0, str(_src_root))
@@ -3,5 +3,5 @@
# Name: __init__.py - {{SKILL_NAME}} apps package
# Date: 2026-03-07
# Version: 1.0.0
# Category: skills/catalog/{{SKILL_NAME}}/apps
# Category: skills/{{SKILL_NAME}}/apps
# =============================================
@@ -3,7 +3,7 @@
# Name: __init__.py - {{SKILL_NAME}} handlers package
# Date: 2026-03-07
# Version: 1.0.0
# Category: skills/catalog/{{SKILL_NAME}}/apps/handlers
# Category: skills/{{SKILL_NAME}}/apps/handlers
#
# CHANGELOG (Max 5 entries):
# - v1.0.0 (2026-03-07): Initial scaffold
@@ -3,7 +3,7 @@
# Name: __init__.py - {{SKILL_NAME}} modules package
# Date: 2026-03-07
# Version: 1.0.0
# Category: skills/catalog/{{SKILL_NAME}}/apps/modules
# Category: skills/{{SKILL_NAME}}/apps/modules
#
# CHANGELOG (Max 5 entries):
# - v1.0.0 (2026-03-07): Initial scaffold
+3 -3
View File
@@ -142,7 +142,7 @@ class TestParseSimpleValue:
class TestDiscoverSkillsInPath:
def test_finds_catalog_skills(self):
catalog_path = Path(__file__).resolve().parent.parent / "catalog"
catalog_path = Path(__file__).resolve().parent.parent / "lib"
skills = discover_skills_in_path(catalog_path, "builtin")
names = {s["name"] for s in skills}
assert "github" in names
@@ -159,7 +159,7 @@ class TestDiscoverSkillsInPath:
assert skills == []
def test_skill_dict_structure(self):
catalog_path = Path(__file__).resolve().parent.parent / "catalog"
catalog_path = Path(__file__).resolve().parent.parent / "lib"
skills = discover_skills_in_path(catalog_path, "builtin")
for skill in skills:
assert "name" in skill
@@ -170,7 +170,7 @@ class TestDiscoverSkillsInPath:
assert "tags" in skill
def test_has_handler_flag(self):
catalog_path = Path(__file__).resolve().parent.parent / "catalog"
catalog_path = Path(__file__).resolve().parent.parent / "lib"
skills = discover_skills_in_path(catalog_path, "builtin")
skill_map = {s["name"]: s for s in skills}
assert skill_map["github"]["has_handler"] is False
+1 -1
View File
@@ -106,7 +106,7 @@ class TestCatalogSkillsLifecycle:
def test_github_skill_full_cycle(self):
"""GitHub (Tier 1): discover -> load -> run returns instructions."""
catalog = Path(__file__).resolve().parent.parent / "catalog"
catalog = Path(__file__).resolve().parent.parent / "lib"
skills = discover_skills_in_path(catalog, "builtin")
github = [s for s in skills if s["name"] == "github"]
assert len(github) == 1