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:
@@ -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
|
||||
```
|
||||
@@ -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."
|
||||
},
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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():
|
||||
|
||||
+2
-2
@@ -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]
|
||||
|
||||
|
||||
+1
-1
@@ -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
|
||||
+1
-1
@@ -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
|
||||
+1
-1
@@ -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
|
||||
+1
-1
@@ -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
|
||||
+1
-1
@@ -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
|
||||
+1
-1
@@ -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
|
||||
+1
-1
@@ -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
|
||||
+2
-2
@@ -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]
|
||||
|
||||
|
||||
+1
-1
@@ -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
|
||||
# =============================================
|
||||
|
||||
"""
|
||||
+1
-1
@@ -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
|
||||
# =============================================
|
||||
+1
-1
@@ -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
|
||||
+1
-1
@@ -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
|
||||
+1
-1
@@ -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
|
||||
+1
-1
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user