tqn-agent-forge 1.0.1__tar.gz
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.
- tqn_agent_forge-1.0.1/.gitignore +143 -0
- tqn_agent_forge-1.0.1/PKG-INFO +109 -0
- tqn_agent_forge-1.0.1/README.md +87 -0
- tqn_agent_forge-1.0.1/agent_forge/__init__.py +3 -0
- tqn_agent_forge-1.0.1/agent_forge/canonical.py +116 -0
- tqn_agent_forge-1.0.1/agent_forge/cli.py +276 -0
- tqn_agent_forge-1.0.1/agent_forge/detectors.py +17 -0
- tqn_agent_forge-1.0.1/agent_forge/frontmatter.py +23 -0
- tqn_agent_forge-1.0.1/agent_forge/github.py +36 -0
- tqn_agent_forge-1.0.1/agent_forge/manifest.py +96 -0
- tqn_agent_forge-1.0.1/agent_forge/translators/__init__.py +38 -0
- tqn_agent_forge-1.0.1/agent_forge/translators/_base.py +36 -0
- tqn_agent_forge-1.0.1/agent_forge/translators/amp.py +37 -0
- tqn_agent_forge-1.0.1/agent_forge/translators/claude_code.py +34 -0
- tqn_agent_forge-1.0.1/agent_forge/translators/codex_cli.py +80 -0
- tqn_agent_forge-1.0.1/agent_forge/translators/copilot_cli.py +78 -0
- tqn_agent_forge-1.0.1/agent_forge/translators/crush.py +47 -0
- tqn_agent_forge-1.0.1/agent_forge/translators/cursor.py +63 -0
- tqn_agent_forge-1.0.1/agent_forge/translators/gemini_cli.py +37 -0
- tqn_agent_forge-1.0.1/agent_forge/translators/kilocode.py +47 -0
- tqn_agent_forge-1.0.1/agent_forge/translators/opencode.py +47 -0
- tqn_agent_forge-1.0.1/agent_forge/translators/prompt_loader.py +83 -0
- tqn_agent_forge-1.0.1/pyproject.toml +37 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# Logs
|
|
2
|
+
logs
|
|
3
|
+
*.log
|
|
4
|
+
npm-debug.log*
|
|
5
|
+
yarn-debug.log*
|
|
6
|
+
yarn-error.log*
|
|
7
|
+
lerna-debug.log*
|
|
8
|
+
|
|
9
|
+
# Diagnostic reports (https://nodejs.org/api/report.html)
|
|
10
|
+
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
|
11
|
+
|
|
12
|
+
# Runtime data
|
|
13
|
+
pids
|
|
14
|
+
*.pid
|
|
15
|
+
*.seed
|
|
16
|
+
*.pid.lock
|
|
17
|
+
|
|
18
|
+
# Directory for instrumented libs generated by jscoverage/JSCover
|
|
19
|
+
lib-cov
|
|
20
|
+
|
|
21
|
+
# Coverage directory used by tools like istanbul
|
|
22
|
+
coverage
|
|
23
|
+
*.lcov
|
|
24
|
+
|
|
25
|
+
# nyc test coverage
|
|
26
|
+
.nyc_output
|
|
27
|
+
|
|
28
|
+
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
|
29
|
+
.grunt
|
|
30
|
+
|
|
31
|
+
# Bower dependency directory (https://bower.io/)
|
|
32
|
+
bower_components
|
|
33
|
+
|
|
34
|
+
# node-waf configuration
|
|
35
|
+
.lock-wscript
|
|
36
|
+
|
|
37
|
+
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
|
38
|
+
build/Release
|
|
39
|
+
|
|
40
|
+
# Dependency directories
|
|
41
|
+
node_modules/
|
|
42
|
+
jspm_packages/
|
|
43
|
+
|
|
44
|
+
# Snowpack dependency directory (https://snowpack.dev/)
|
|
45
|
+
web_modules/
|
|
46
|
+
|
|
47
|
+
# TypeScript cache
|
|
48
|
+
*.tsbuildinfo
|
|
49
|
+
|
|
50
|
+
# Optional npm cache directory
|
|
51
|
+
.npm
|
|
52
|
+
|
|
53
|
+
# Optional eslint cache
|
|
54
|
+
.eslintcache
|
|
55
|
+
|
|
56
|
+
# Optional stylelint cache
|
|
57
|
+
.stylelintcache
|
|
58
|
+
|
|
59
|
+
# Optional REPL history
|
|
60
|
+
.node_repl_history
|
|
61
|
+
|
|
62
|
+
# Output of 'npm pack'
|
|
63
|
+
*.tgz
|
|
64
|
+
|
|
65
|
+
# Yarn Integrity file
|
|
66
|
+
.yarn-integrity
|
|
67
|
+
|
|
68
|
+
# dotenv environment variable files
|
|
69
|
+
.env
|
|
70
|
+
.env.*
|
|
71
|
+
!.env.example
|
|
72
|
+
|
|
73
|
+
# parcel-bundler cache (https://parceljs.org/)
|
|
74
|
+
.cache
|
|
75
|
+
.parcel-cache
|
|
76
|
+
|
|
77
|
+
# Next.js build output
|
|
78
|
+
.next
|
|
79
|
+
out
|
|
80
|
+
|
|
81
|
+
# Nuxt.js build / generate output
|
|
82
|
+
.nuxt
|
|
83
|
+
dist
|
|
84
|
+
.output
|
|
85
|
+
|
|
86
|
+
# Gatsby files
|
|
87
|
+
.cache/
|
|
88
|
+
# Comment in the public line in if your project uses Gatsby and not Next.js
|
|
89
|
+
# https://nextjs.org/blog/next-9-1#public-directory-support
|
|
90
|
+
# public
|
|
91
|
+
|
|
92
|
+
# vuepress build output
|
|
93
|
+
.vuepress/dist
|
|
94
|
+
|
|
95
|
+
# vuepress v2.x temp directory
|
|
96
|
+
.temp
|
|
97
|
+
|
|
98
|
+
# Sveltekit cache directory
|
|
99
|
+
.svelte-kit/
|
|
100
|
+
|
|
101
|
+
# vitepress build output
|
|
102
|
+
**/.vitepress/dist
|
|
103
|
+
|
|
104
|
+
# vitepress cache directory
|
|
105
|
+
**/.vitepress/cache
|
|
106
|
+
|
|
107
|
+
# Docusaurus cache and generated files
|
|
108
|
+
.docusaurus
|
|
109
|
+
|
|
110
|
+
# Serverless directories
|
|
111
|
+
.serverless/
|
|
112
|
+
|
|
113
|
+
# FuseBox cache
|
|
114
|
+
.fusebox/
|
|
115
|
+
|
|
116
|
+
# DynamoDB Local files
|
|
117
|
+
.dynamodb/
|
|
118
|
+
|
|
119
|
+
# Firebase cache directory
|
|
120
|
+
.firebase/
|
|
121
|
+
|
|
122
|
+
# TernJS port file
|
|
123
|
+
.tern-port
|
|
124
|
+
|
|
125
|
+
# Stores VSCode versions used for testing VSCode extensions
|
|
126
|
+
.vscode-test
|
|
127
|
+
|
|
128
|
+
# pnpm
|
|
129
|
+
.pnpm-store
|
|
130
|
+
|
|
131
|
+
# yarn v3
|
|
132
|
+
.pnp.*
|
|
133
|
+
.yarn/*
|
|
134
|
+
!.yarn/patches
|
|
135
|
+
!.yarn/plugins
|
|
136
|
+
!.yarn/releases
|
|
137
|
+
!.yarn/sdks
|
|
138
|
+
!.yarn/versions
|
|
139
|
+
|
|
140
|
+
# Vite files
|
|
141
|
+
vite.config.js.timestamp-*
|
|
142
|
+
vite.config.ts.timestamp-*
|
|
143
|
+
.vite/
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tqn-agent-forge
|
|
3
|
+
Version: 1.0.1
|
|
4
|
+
Summary: Cross-CLI plugin marketplace installer for agent-forge
|
|
5
|
+
Project-URL: Homepage, https://github.com/tqnonline/agent-forge
|
|
6
|
+
Project-URL: Repository, https://github.com/tqnonline/agent-forge
|
|
7
|
+
Project-URL: Issues, https://github.com/tqnonline/agent-forge/issues
|
|
8
|
+
Author-email: Rahul N Akmol <rahulnakmol@gmail.com>
|
|
9
|
+
License: BSD-3-Clause
|
|
10
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
11
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Requires-Python: >=3.11
|
|
15
|
+
Requires-Dist: click>=8.1
|
|
16
|
+
Requires-Dist: httpx>=0.27
|
|
17
|
+
Requires-Dist: platformdirs>=4.0
|
|
18
|
+
Requires-Dist: pydantic>=2.0
|
|
19
|
+
Requires-Dist: pyyaml>=6.0
|
|
20
|
+
Requires-Dist: tomli-w>=1.0
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
|
|
23
|
+
# agent-forge
|
|
24
|
+
|
|
25
|
+
[](https://github.com/tqnonline/agent-forge/releases/latest)
|
|
26
|
+
[](https://pypi.org/project/tqn-agent-forge/)
|
|
27
|
+
[](LICENSE)
|
|
28
|
+
[](https://github.com/tqnonline/agent-forge/actions/workflows/ci-structural.yml)
|
|
29
|
+
|
|
30
|
+
A curated, BSD-3-Clause marketplace of agent skills, plugins, and slash commands.
|
|
31
|
+
Works natively with Claude Code, GitHub Copilot CLI, OpenAI Codex CLI, and Cursor.
|
|
32
|
+
Git-URL install for Amp and Gemini CLI. Copy-to-home adapters for Kilo Code,
|
|
33
|
+
OpenCode, and Crush. Portable prompt loaders for Perplexity, ChatGPT GPTs,
|
|
34
|
+
and Claude.ai Projects.
|
|
35
|
+
|
|
36
|
+
**12 install targets. 4 plugins. 46 skills. One canonical source.**
|
|
37
|
+
|
|
38
|
+
## 30-second install
|
|
39
|
+
|
|
40
|
+
**Claude Code:**
|
|
41
|
+
```bash
|
|
42
|
+
claude plugin marketplace add github:tqnonline/agent-forge
|
|
43
|
+
claude plugin install writing
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**GitHub Copilot CLI:**
|
|
47
|
+
```bash
|
|
48
|
+
copilot plugin marketplace add tqnonline/agent-forge
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**Codex CLI:**
|
|
52
|
+
```bash
|
|
53
|
+
codex plugin marketplace add tqnonline/agent-forge
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**Cursor:** Open `cursor.com/marketplace`, search "agent-forge", click Install.
|
|
57
|
+
|
|
58
|
+
**Amp:**
|
|
59
|
+
```bash
|
|
60
|
+
amp skill add github:tqnonline/agent-forge
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Gemini CLI:**
|
|
64
|
+
```bash
|
|
65
|
+
gemini skills install github:tqnonline/agent-forge
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Kilo Code, OpenCode, or Crush:**
|
|
69
|
+
```bash
|
|
70
|
+
pipx install tqn-agent-forge
|
|
71
|
+
agent-forge install writing --tier kilocode # or opencode, or crush
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**Perplexity, ChatGPT GPTs, Claude.ai Projects:** See [docs/install/_index.md](./docs/install/_index.md) — these use prompt loaders rather than native install.
|
|
75
|
+
|
|
76
|
+
## What's in the marketplace
|
|
77
|
+
|
|
78
|
+
| Plugin | Skills | Description |
|
|
79
|
+
|---|---|---|
|
|
80
|
+
| `writing` | humanize | Detect AI writing patterns and rewrite in natural human voice |
|
|
81
|
+
| `prompts` | prompt-forge | Interactive prompt engineering via structured dialogue |
|
|
82
|
+
| `msft-arch` | 32 skills | Microsoft enterprise architecture: Azure, M365, Power Platform, Dynamics 365 |
|
|
83
|
+
| `pm` | 12 skills | Product/Program Management: PRD generation, epic decomposition, 11-Star review |
|
|
84
|
+
|
|
85
|
+
## Telling an agent to install
|
|
86
|
+
|
|
87
|
+
Paste this into any LLM agent:
|
|
88
|
+
|
|
89
|
+
> Install agent-forge plugins from
|
|
90
|
+
> https://raw.githubusercontent.com/tqnonline/agent-forge/main/docs/install/_index.md
|
|
91
|
+
|
|
92
|
+
The agent fetches the catalog, identifies your tool, and runs the correct install commands.
|
|
93
|
+
|
|
94
|
+
## Updating
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
agent-forge update --check # see what's stale
|
|
98
|
+
agent-forge update # apply
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Contributing
|
|
102
|
+
|
|
103
|
+
See [CONTRIBUTING.md](./CONTRIBUTING.md). Three paths: typo fix (no issue needed),
|
|
104
|
+
new skill (must include eval suite), new plugin (requires ADR discussion). DCO sign-off
|
|
105
|
+
required (`git commit -s`).
|
|
106
|
+
|
|
107
|
+
## License
|
|
108
|
+
|
|
109
|
+
BSD-3-Clause. Per-plugin assets may carry their own licenses; see `THIRD_PARTY_NOTICES.md`.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# agent-forge
|
|
2
|
+
|
|
3
|
+
[](https://github.com/tqnonline/agent-forge/releases/latest)
|
|
4
|
+
[](https://pypi.org/project/tqn-agent-forge/)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](https://github.com/tqnonline/agent-forge/actions/workflows/ci-structural.yml)
|
|
7
|
+
|
|
8
|
+
A curated, BSD-3-Clause marketplace of agent skills, plugins, and slash commands.
|
|
9
|
+
Works natively with Claude Code, GitHub Copilot CLI, OpenAI Codex CLI, and Cursor.
|
|
10
|
+
Git-URL install for Amp and Gemini CLI. Copy-to-home adapters for Kilo Code,
|
|
11
|
+
OpenCode, and Crush. Portable prompt loaders for Perplexity, ChatGPT GPTs,
|
|
12
|
+
and Claude.ai Projects.
|
|
13
|
+
|
|
14
|
+
**12 install targets. 4 plugins. 46 skills. One canonical source.**
|
|
15
|
+
|
|
16
|
+
## 30-second install
|
|
17
|
+
|
|
18
|
+
**Claude Code:**
|
|
19
|
+
```bash
|
|
20
|
+
claude plugin marketplace add github:tqnonline/agent-forge
|
|
21
|
+
claude plugin install writing
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
**GitHub Copilot CLI:**
|
|
25
|
+
```bash
|
|
26
|
+
copilot plugin marketplace add tqnonline/agent-forge
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
**Codex CLI:**
|
|
30
|
+
```bash
|
|
31
|
+
codex plugin marketplace add tqnonline/agent-forge
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**Cursor:** Open `cursor.com/marketplace`, search "agent-forge", click Install.
|
|
35
|
+
|
|
36
|
+
**Amp:**
|
|
37
|
+
```bash
|
|
38
|
+
amp skill add github:tqnonline/agent-forge
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**Gemini CLI:**
|
|
42
|
+
```bash
|
|
43
|
+
gemini skills install github:tqnonline/agent-forge
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**Kilo Code, OpenCode, or Crush:**
|
|
47
|
+
```bash
|
|
48
|
+
pipx install tqn-agent-forge
|
|
49
|
+
agent-forge install writing --tier kilocode # or opencode, or crush
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**Perplexity, ChatGPT GPTs, Claude.ai Projects:** See [docs/install/_index.md](./docs/install/_index.md) — these use prompt loaders rather than native install.
|
|
53
|
+
|
|
54
|
+
## What's in the marketplace
|
|
55
|
+
|
|
56
|
+
| Plugin | Skills | Description |
|
|
57
|
+
|---|---|---|
|
|
58
|
+
| `writing` | humanize | Detect AI writing patterns and rewrite in natural human voice |
|
|
59
|
+
| `prompts` | prompt-forge | Interactive prompt engineering via structured dialogue |
|
|
60
|
+
| `msft-arch` | 32 skills | Microsoft enterprise architecture: Azure, M365, Power Platform, Dynamics 365 |
|
|
61
|
+
| `pm` | 12 skills | Product/Program Management: PRD generation, epic decomposition, 11-Star review |
|
|
62
|
+
|
|
63
|
+
## Telling an agent to install
|
|
64
|
+
|
|
65
|
+
Paste this into any LLM agent:
|
|
66
|
+
|
|
67
|
+
> Install agent-forge plugins from
|
|
68
|
+
> https://raw.githubusercontent.com/tqnonline/agent-forge/main/docs/install/_index.md
|
|
69
|
+
|
|
70
|
+
The agent fetches the catalog, identifies your tool, and runs the correct install commands.
|
|
71
|
+
|
|
72
|
+
## Updating
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
agent-forge update --check # see what's stale
|
|
76
|
+
agent-forge update # apply
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Contributing
|
|
80
|
+
|
|
81
|
+
See [CONTRIBUTING.md](./CONTRIBUTING.md). Three paths: typo fix (no issue needed),
|
|
82
|
+
new skill (must include eval suite), new plugin (requires ADR discussion). DCO sign-off
|
|
83
|
+
required (`git commit -s`).
|
|
84
|
+
|
|
85
|
+
## License
|
|
86
|
+
|
|
87
|
+
BSD-3-Clause. Per-plugin assets may carry their own licenses; see `THIRD_PARTY_NOTICES.md`.
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"""Read the canonical plugin tree under plugins/<name>/."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from functools import cached_property
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from agent_forge.frontmatter import parse_frontmatter
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass(frozen=True)
|
|
12
|
+
class CanonicalSkill:
|
|
13
|
+
plugin_name: str
|
|
14
|
+
name: str
|
|
15
|
+
skill_dir: Path
|
|
16
|
+
|
|
17
|
+
@cached_property
|
|
18
|
+
def skill_md(self) -> Path:
|
|
19
|
+
return self.skill_dir / "SKILL.md"
|
|
20
|
+
|
|
21
|
+
@cached_property
|
|
22
|
+
def frontmatter(self) -> dict:
|
|
23
|
+
fm, _ = parse_frontmatter(self.skill_md.read_text())
|
|
24
|
+
return fm
|
|
25
|
+
|
|
26
|
+
@cached_property
|
|
27
|
+
def body(self) -> str:
|
|
28
|
+
_, body = parse_frontmatter(self.skill_md.read_text())
|
|
29
|
+
return body
|
|
30
|
+
|
|
31
|
+
def references(self) -> list[Path]:
|
|
32
|
+
ref_dir = self.skill_dir / "references"
|
|
33
|
+
return sorted(ref_dir.rglob("*.md")) if ref_dir.exists() else []
|
|
34
|
+
|
|
35
|
+
def scripts(self) -> list[Path]:
|
|
36
|
+
sd = self.skill_dir / "scripts"
|
|
37
|
+
return sorted(sd.rglob("*")) if sd.exists() else []
|
|
38
|
+
|
|
39
|
+
def assets(self) -> list[Path]:
|
|
40
|
+
ad = self.skill_dir / "assets"
|
|
41
|
+
return sorted(ad.rglob("*")) if ad.exists() else []
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass(frozen=True)
|
|
45
|
+
class CanonicalAgent:
|
|
46
|
+
plugin_name: str
|
|
47
|
+
name: str
|
|
48
|
+
md_path: Path
|
|
49
|
+
|
|
50
|
+
@cached_property
|
|
51
|
+
def frontmatter(self) -> dict:
|
|
52
|
+
fm, _ = parse_frontmatter(self.md_path.read_text())
|
|
53
|
+
return fm
|
|
54
|
+
|
|
55
|
+
@cached_property
|
|
56
|
+
def body(self) -> str:
|
|
57
|
+
_, body = parse_frontmatter(self.md_path.read_text())
|
|
58
|
+
return body
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@dataclass(frozen=True)
|
|
62
|
+
class CanonicalCommand:
|
|
63
|
+
plugin_name: str
|
|
64
|
+
name: str
|
|
65
|
+
md_path: Path
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@dataclass(frozen=True)
|
|
69
|
+
class CanonicalPlugin:
|
|
70
|
+
plugin_dir: Path
|
|
71
|
+
|
|
72
|
+
@property
|
|
73
|
+
def name(self) -> str:
|
|
74
|
+
return self.plugin_dir.name
|
|
75
|
+
|
|
76
|
+
@cached_property
|
|
77
|
+
def manifest(self) -> dict:
|
|
78
|
+
return json.loads((self.plugin_dir / ".claude-plugin" / "plugin.json").read_text())
|
|
79
|
+
|
|
80
|
+
def skills(self) -> list[CanonicalSkill]:
|
|
81
|
+
skills_dir = self.plugin_dir / "skills"
|
|
82
|
+
if not skills_dir.exists():
|
|
83
|
+
return []
|
|
84
|
+
result = []
|
|
85
|
+
for sd in sorted(skills_dir.iterdir()):
|
|
86
|
+
if (sd / "SKILL.md").exists():
|
|
87
|
+
result.append(CanonicalSkill(self.name, sd.name, sd))
|
|
88
|
+
return result
|
|
89
|
+
|
|
90
|
+
def agents(self) -> list[CanonicalAgent]:
|
|
91
|
+
ad = self.plugin_dir / "agents"
|
|
92
|
+
if not ad.exists():
|
|
93
|
+
return []
|
|
94
|
+
return [
|
|
95
|
+
CanonicalAgent(self.name, p.stem, p)
|
|
96
|
+
for p in sorted(ad.glob("*.md"))
|
|
97
|
+
]
|
|
98
|
+
|
|
99
|
+
def commands(self) -> list[CanonicalCommand]:
|
|
100
|
+
cd = self.plugin_dir / "commands"
|
|
101
|
+
if not cd.exists():
|
|
102
|
+
return []
|
|
103
|
+
return [
|
|
104
|
+
CanonicalCommand(self.name, p.stem, p)
|
|
105
|
+
for p in sorted(cd.glob("*.md"))
|
|
106
|
+
]
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def discover_plugins(plugins_dir: Path) -> list[CanonicalPlugin]:
|
|
110
|
+
"""All plugins discovered under plugins/<name>/.claude-plugin/plugin.json."""
|
|
111
|
+
if not plugins_dir.exists():
|
|
112
|
+
return []
|
|
113
|
+
return [
|
|
114
|
+
CanonicalPlugin(p.parent.parent)
|
|
115
|
+
for p in sorted(plugins_dir.glob("*/.claude-plugin/plugin.json"))
|
|
116
|
+
]
|