@orderful/droid 0.25.2 → 0.27.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.
- package/.claude-plugin/marketplace.json +114 -0
- package/.github/workflows/ci.yml +20 -0
- package/AGENTS.md +75 -18
- package/CHANGELOG.md +73 -0
- package/README.md +30 -4
- package/dist/bin/droid.js +164 -75
- package/dist/index.js +68 -26
- package/dist/lib/migrations.d.ts +8 -0
- package/dist/lib/migrations.d.ts.map +1 -1
- package/dist/lib/skill-config.d.ts.map +1 -1
- package/dist/lib/skills.d.ts.map +1 -1
- package/dist/lib/tools.d.ts.map +1 -1
- package/dist/lib/types.d.ts +10 -2
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/tools/README.md +1 -1
- package/dist/tools/brain/.claude-plugin/plugin.json +16 -0
- package/dist/tools/brain/TOOL.yaml +7 -5
- package/dist/tools/brain/commands/brain.md +17 -49
- package/dist/tools/brain/commands/scratchpad.md +13 -50
- package/{src/tools/brain/skills/droid-brain → dist/tools/brain/skills/brain}/SKILL.md +6 -6
- package/dist/tools/brain/skills/{droid-brain-obsidian → brain-obsidian}/SKILL.md +9 -9
- package/dist/tools/coach/.claude-plugin/plugin.json +16 -0
- package/dist/tools/coach/TOOL.yaml +4 -3
- package/dist/tools/coach/commands/coach.md +14 -54
- package/{src/tools/coach/skills/droid-coach → dist/tools/coach/skills/coach}/SKILL.md +5 -4
- package/dist/tools/code-review/.claude-plugin/plugin.json +16 -0
- package/dist/tools/code-review/TOOL.yaml +4 -3
- package/dist/tools/code-review/commands/code-review.md +18 -102
- package/dist/tools/code-review/skills/code-review/SKILL.md +154 -0
- package/dist/tools/codex/.claude-plugin/plugin.json +16 -0
- package/dist/tools/codex/TOOL.yaml +5 -4
- package/dist/tools/codex/commands/codex.md +18 -65
- package/dist/tools/codex/skills/{droid-codex → codex}/SKILL.md +64 -45
- package/{src/tools/codex/skills/droid-codex → dist/tools/codex/skills/codex}/references/loading.md +94 -55
- package/dist/tools/codex/skills/codex/scripts/git-finish-write.d.ts.map +1 -0
- package/dist/tools/codex/skills/codex/scripts/git-preamble.d.ts.map +1 -0
- package/dist/tools/codex/skills/codex/scripts/git-start-write.d.ts.map +1 -0
- package/dist/tools/comments/.claude-plugin/plugin.json +16 -0
- package/dist/tools/comments/TOOL.yaml +4 -3
- package/dist/tools/comments/commands/comments.md +12 -14
- package/{src/tools/comments/skills/droid-comments → dist/tools/comments/skills/comments}/SKILL.md +4 -2
- package/dist/tools/droid/.claude-plugin/plugin.json +15 -0
- package/dist/tools/droid/TOOL.yaml +1 -1
- package/dist/tools/droid/skills/droid/SKILL.md +1 -1
- package/dist/tools/project/.claude-plugin/plugin.json +16 -0
- package/dist/tools/project/TOOL.yaml +4 -3
- package/dist/tools/project/commands/project.md +12 -27
- package/dist/tools/project/skills/{droid-project → project}/SKILL.md +13 -12
- package/dist/tools/tech-design/.claude-plugin/plugin.json +16 -0
- package/dist/tools/tech-design/TOOL.yaml +19 -0
- package/dist/tools/tech-design/commands/tech-design.md +31 -0
- package/dist/tools/tech-design/skills/tech-design/SKILL.md +218 -0
- package/dist/tools/tech-design/skills/tech-design/references/draft.md +321 -0
- package/dist/tools/tech-design/skills/tech-design/references/gaps.md +328 -0
- package/dist/tools/tech-design/skills/tech-design/references/publish.md +409 -0
- package/dist/tools/tech-design/skills/tech-design/references/research-doc-template.md +129 -0
- package/dist/tools/tech-design/skills/tech-design/references/rollup-template.md +55 -0
- package/dist/tools/tech-design/skills/tech-design/references/start.md +353 -0
- package/dist/tools/tech-design/skills/tech-design/references/think.md +356 -0
- package/dist/tools/tech-design/skills/tech-design/references/thought-doc-template.md +72 -0
- package/package.json +3 -2
- package/scripts/build-plugins.ts +207 -0
- package/src/commands/tui/components/Badge.test.tsx +10 -4
- package/src/commands/tui.tsx +4 -4
- package/src/lib/migrations.ts +95 -4
- package/src/lib/skill-config.ts +95 -57
- package/src/lib/skills.test.ts +199 -74
- package/src/lib/skills.ts +57 -56
- package/src/lib/tools.ts +19 -12
- package/src/lib/types.ts +20 -5
- package/src/tools/README.md +1 -1
- package/src/tools/brain/.claude-plugin/plugin.json +16 -0
- package/src/tools/brain/TOOL.yaml +7 -5
- package/src/tools/brain/commands/brain.md +17 -49
- package/src/tools/brain/commands/scratchpad.md +13 -50
- package/{dist/tools/brain/skills/droid-brain → src/tools/brain/skills/brain}/SKILL.md +6 -6
- package/src/tools/brain/skills/{droid-brain-obsidian → brain-obsidian}/SKILL.md +9 -9
- package/src/tools/coach/.claude-plugin/plugin.json +16 -0
- package/src/tools/coach/TOOL.yaml +4 -3
- package/src/tools/coach/commands/coach.md +14 -54
- package/{dist/tools/coach/skills/droid-coach → src/tools/coach/skills/coach}/SKILL.md +5 -4
- package/src/tools/code-review/.claude-plugin/plugin.json +16 -0
- package/src/tools/code-review/TOOL.yaml +4 -3
- package/src/tools/code-review/commands/code-review.md +18 -102
- package/src/tools/code-review/skills/code-review/SKILL.md +154 -0
- package/src/tools/codex/.claude-plugin/plugin.json +16 -0
- package/src/tools/codex/TOOL.yaml +5 -4
- package/src/tools/codex/commands/codex.md +18 -65
- package/src/tools/codex/skills/{droid-codex → codex}/SKILL.md +64 -45
- package/{dist/tools/codex/skills/droid-codex → src/tools/codex/skills/codex}/references/loading.md +94 -55
- package/src/tools/comments/.claude-plugin/plugin.json +16 -0
- package/src/tools/comments/TOOL.yaml +4 -3
- package/src/tools/comments/commands/comments.md +12 -14
- package/{dist/tools/comments/skills/droid-comments → src/tools/comments/skills/comments}/SKILL.md +4 -2
- package/src/tools/droid/.claude-plugin/plugin.json +15 -0
- package/src/tools/droid/TOOL.yaml +1 -1
- package/src/tools/droid/skills/droid/SKILL.md +1 -1
- package/src/tools/project/.claude-plugin/plugin.json +16 -0
- package/src/tools/project/TOOL.yaml +4 -3
- package/src/tools/project/commands/project.md +12 -27
- package/src/tools/project/skills/{droid-project → project}/SKILL.md +13 -12
- package/src/tools/tech-design/.claude-plugin/plugin.json +16 -0
- package/src/tools/tech-design/TOOL.yaml +19 -0
- package/src/tools/tech-design/commands/tech-design.md +31 -0
- package/src/tools/tech-design/skills/tech-design/SKILL.md +218 -0
- package/src/tools/tech-design/skills/tech-design/references/draft.md +321 -0
- package/src/tools/tech-design/skills/tech-design/references/gaps.md +328 -0
- package/src/tools/tech-design/skills/tech-design/references/publish.md +409 -0
- package/src/tools/tech-design/skills/tech-design/references/research-doc-template.md +129 -0
- package/src/tools/tech-design/skills/tech-design/references/rollup-template.md +55 -0
- package/src/tools/tech-design/skills/tech-design/references/start.md +353 -0
- package/src/tools/tech-design/skills/tech-design/references/think.md +356 -0
- package/src/tools/tech-design/skills/tech-design/references/thought-doc-template.md +72 -0
- package/dist/tools/code-review/skills/droid-code-review/SKILL.md +0 -55
- package/dist/tools/codex/skills/droid-codex/scripts/git-finish-write.d.ts.map +0 -1
- package/dist/tools/codex/skills/droid-codex/scripts/git-preamble.d.ts.map +0 -1
- package/dist/tools/codex/skills/droid-codex/scripts/git-start-write.d.ts.map +0 -1
- package/src/tools/code-review/skills/droid-code-review/SKILL.md +0 -55
- /package/dist/tools/brain/skills/{droid-brain → brain}/references/metadata.md +0 -0
- /package/dist/tools/brain/skills/{droid-brain → brain}/references/naming.md +0 -0
- /package/dist/tools/brain/skills/{droid-brain → brain}/references/templates.md +0 -0
- /package/dist/tools/brain/skills/{droid-brain → brain}/references/workflows.md +0 -0
- /package/dist/tools/brain/skills/{droid-brain-obsidian → brain-obsidian}/references/templates.md +0 -0
- /package/dist/tools/brain/skills/{droid-brain-obsidian → brain-obsidian}/references/workflows.md +0 -0
- /package/dist/tools/codex/skills/{droid-codex → codex}/references/creating.md +0 -0
- /package/dist/tools/codex/skills/{droid-codex → codex}/references/decisions.md +0 -0
- /package/dist/tools/codex/skills/{droid-codex → codex}/references/topics.md +0 -0
- /package/dist/tools/codex/skills/{droid-codex → codex}/scripts/git-finish-write.d.ts +0 -0
- /package/dist/tools/codex/skills/{droid-codex → codex}/scripts/git-finish-write.ts +0 -0
- /package/dist/tools/codex/skills/{droid-codex → codex}/scripts/git-preamble.d.ts +0 -0
- /package/dist/tools/codex/skills/{droid-codex → codex}/scripts/git-preamble.ts +0 -0
- /package/dist/tools/codex/skills/{droid-codex → codex}/scripts/git-scripts.test.ts +0 -0
- /package/dist/tools/codex/skills/{droid-codex → codex}/scripts/git-start-write.d.ts +0 -0
- /package/dist/tools/codex/skills/{droid-codex → codex}/scripts/git-start-write.ts +0 -0
- /package/dist/tools/project/skills/{droid-project → project}/references/changelog.md +0 -0
- /package/dist/tools/project/skills/{droid-project → project}/references/creating.md +0 -0
- /package/dist/tools/project/skills/{droid-project → project}/references/loading.md +0 -0
- /package/dist/tools/project/skills/{droid-project → project}/references/templates.md +0 -0
- /package/dist/tools/project/skills/{droid-project → project}/references/updating.md +0 -0
- /package/dist/tools/project/skills/{droid-project → project}/references/versioning.md +0 -0
- /package/src/tools/brain/skills/{droid-brain → brain}/references/metadata.md +0 -0
- /package/src/tools/brain/skills/{droid-brain → brain}/references/naming.md +0 -0
- /package/src/tools/brain/skills/{droid-brain → brain}/references/templates.md +0 -0
- /package/src/tools/brain/skills/{droid-brain → brain}/references/workflows.md +0 -0
- /package/src/tools/brain/skills/{droid-brain-obsidian → brain-obsidian}/references/templates.md +0 -0
- /package/src/tools/brain/skills/{droid-brain-obsidian → brain-obsidian}/references/workflows.md +0 -0
- /package/src/tools/codex/skills/{droid-codex → codex}/references/creating.md +0 -0
- /package/src/tools/codex/skills/{droid-codex → codex}/references/decisions.md +0 -0
- /package/src/tools/codex/skills/{droid-codex → codex}/references/topics.md +0 -0
- /package/src/tools/codex/skills/{droid-codex → codex}/scripts/git-finish-write.ts +0 -0
- /package/src/tools/codex/skills/{droid-codex → codex}/scripts/git-preamble.ts +0 -0
- /package/src/tools/codex/skills/{droid-codex → codex}/scripts/git-scripts.test.ts +0 -0
- /package/src/tools/codex/skills/{droid-codex → codex}/scripts/git-start-write.ts +0 -0
- /package/src/tools/project/skills/{droid-project → project}/references/changelog.md +0 -0
- /package/src/tools/project/skills/{droid-project → project}/references/creating.md +0 -0
- /package/src/tools/project/skills/{droid-project → project}/references/loading.md +0 -0
- /package/src/tools/project/skills/{droid-project → project}/references/templates.md +0 -0
- /package/src/tools/project/skills/{droid-project → project}/references/updating.md +0 -0
- /package/src/tools/project/skills/{droid-project → project}/references/versioning.md +0 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Thought Doc Template
|
|
2
|
+
|
|
3
|
+
Used when creating thought doc via `/brain plan tech-design-{project}`.
|
|
4
|
+
|
|
5
|
+
```markdown
|
|
6
|
+
# [Tech Design] {Project Name}
|
|
7
|
+
|
|
8
|
+
**Type:** plan
|
|
9
|
+
**Status:** exploring
|
|
10
|
+
**Created:** {YYYY-MM-DD}
|
|
11
|
+
**PRD:** [[codex:projects/{project}/PRD]]
|
|
12
|
+
**Research:** [[tech-design-{project}-research]]
|
|
13
|
+
|
|
14
|
+
Tech design for {project}.
|
|
15
|
+
|
|
16
|
+
## Background
|
|
17
|
+
|
|
18
|
+
{Why we're doing this - 2-3 paragraphs from PRD}
|
|
19
|
+
|
|
20
|
+
{What problem we're solving, who it's for, why it matters}
|
|
21
|
+
|
|
22
|
+
See research doc for codebase context and discovered patterns.
|
|
23
|
+
|
|
24
|
+
## Proposal
|
|
25
|
+
|
|
26
|
+
{What we're building - to be drafted}
|
|
27
|
+
|
|
28
|
+
## Open Questions
|
|
29
|
+
|
|
30
|
+
{What we don't know yet - to be explored}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Variable Substitutions
|
|
34
|
+
|
|
35
|
+
| Variable | Example | Notes |
|
|
36
|
+
| ---------------- | ----------------------- | --------------------------- |
|
|
37
|
+
| `{Project Name}` | "Transaction Templates" | Human-readable project name |
|
|
38
|
+
| `{YYYY-MM-DD}` | "2026-01-10" | Current date |
|
|
39
|
+
| `{project}` | "transaction-templates" | Codex project slug |
|
|
40
|
+
|
|
41
|
+
## Philosophy
|
|
42
|
+
|
|
43
|
+
**Start minimal:** 3 sections (Background, Proposal, Open Questions)
|
|
44
|
+
|
|
45
|
+
**Gaps surface more:** Use `/tech-design gaps` to discover what sections to add (Data Model, API Surface, Rollout Plan, etc.)
|
|
46
|
+
|
|
47
|
+
**Research doc separation:** The thought doc references the research doc but doesn't duplicate codebase discoveries. Keep the thought doc focused on architectural decisions.
|
|
48
|
+
|
|
49
|
+
## Sections That Can Be Added
|
|
50
|
+
|
|
51
|
+
Via `/tech-design draft {section}` or `/tech-design gaps`:
|
|
52
|
+
|
|
53
|
+
- **Scope** - What's in/out explicitly
|
|
54
|
+
- **Data Model** - Tables, schemas, types
|
|
55
|
+
- **API Surface** - Endpoints, methods, interfaces
|
|
56
|
+
- **Implementation Plan** - Step-by-step approach
|
|
57
|
+
- **Key Decisions** - Options considered, what we chose and why
|
|
58
|
+
- **Trade-off Analysis** - Pros/cons of approach
|
|
59
|
+
- **Risks & Mitigations** - What could go wrong, how to handle it
|
|
60
|
+
- **Metrics** - What we'll measure
|
|
61
|
+
- **Rollout Plan** - Phased deployment, feature flags, rollback
|
|
62
|
+
- **Security Considerations** - Auth, validation, audit logging
|
|
63
|
+
|
|
64
|
+
## Template Evolution
|
|
65
|
+
|
|
66
|
+
The template starts with 3 sections, but the thought doc grows based on:
|
|
67
|
+
|
|
68
|
+
1. **User adds via `/draft {section}`** - AI generates section from research
|
|
69
|
+
2. **User explores via `/think {topic}`** - May add sections organically
|
|
70
|
+
3. **User identifies via `/gaps`** - Shows what's missing, suggests adding
|
|
71
|
+
|
|
72
|
+
The minimal start reduces cognitive load. Sections appear when needed.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@orderful/droid",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.27.0",
|
|
4
4
|
"description": "AI workflow toolkit for sharing skills, commands, and agents across the team",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
},
|
|
9
9
|
"main": "dist/index.js",
|
|
10
10
|
"scripts": {
|
|
11
|
-
"build": "bun run lint && bun scripts/build.ts",
|
|
11
|
+
"build": "bun run lint && bun scripts/build.ts && bun scripts/build-plugins.ts",
|
|
12
|
+
"build:plugins": "bun scripts/build-plugins.ts",
|
|
12
13
|
"dev": "tsc --watch",
|
|
13
14
|
"start": "bun dist/bin/droid.js",
|
|
14
15
|
"test": "bun test src/",
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* Build Claude Code plugin manifests from src/tools/
|
|
4
|
+
*
|
|
5
|
+
* This script generates:
|
|
6
|
+
* - src/tools/{tool}/.claude-plugin/plugin.json (from TOOL.yaml)
|
|
7
|
+
* - .claude-plugin/marketplace.json (at repo root)
|
|
8
|
+
*
|
|
9
|
+
* No file duplication - skills, commands, and agents stay in place.
|
|
10
|
+
* The src/tools/ directory serves both TUI and plugin distribution.
|
|
11
|
+
*
|
|
12
|
+
* Note: All tools are published to the marketplace, including those with
|
|
13
|
+
* `system: true` in their TOOL.yaml. System tools can still be installed
|
|
14
|
+
* individually via the plugin system.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { mkdirSync, writeFileSync, existsSync, readdirSync, readFileSync } from 'fs';
|
|
18
|
+
import { join } from 'path';
|
|
19
|
+
import { parse as parseYaml } from 'yaml';
|
|
20
|
+
|
|
21
|
+
interface ToolManifest {
|
|
22
|
+
name: string;
|
|
23
|
+
description: string;
|
|
24
|
+
version: string;
|
|
25
|
+
status?: string;
|
|
26
|
+
system?: boolean;
|
|
27
|
+
includes: {
|
|
28
|
+
skills: Array<{ name: string; required: boolean; description?: string }>;
|
|
29
|
+
commands: string[];
|
|
30
|
+
agents: string[];
|
|
31
|
+
};
|
|
32
|
+
dependencies?: string[];
|
|
33
|
+
config_schema?: Record<string, unknown>;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface PluginJson {
|
|
37
|
+
name: string;
|
|
38
|
+
version: string;
|
|
39
|
+
description: string;
|
|
40
|
+
author: {
|
|
41
|
+
name: string;
|
|
42
|
+
url?: string;
|
|
43
|
+
};
|
|
44
|
+
repository?: string;
|
|
45
|
+
license?: string;
|
|
46
|
+
keywords?: string[];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
interface MarketplacePlugin {
|
|
50
|
+
name: string;
|
|
51
|
+
description: string;
|
|
52
|
+
version: string;
|
|
53
|
+
source: {
|
|
54
|
+
source: string;
|
|
55
|
+
repo: string;
|
|
56
|
+
path: string;
|
|
57
|
+
};
|
|
58
|
+
author: {
|
|
59
|
+
name: string;
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
interface MarketplaceJson {
|
|
64
|
+
name: string;
|
|
65
|
+
description: string;
|
|
66
|
+
owner: {
|
|
67
|
+
name: string;
|
|
68
|
+
url?: string;
|
|
69
|
+
};
|
|
70
|
+
plugins: MarketplacePlugin[];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const TOOLS_DIR = 'src/tools';
|
|
74
|
+
const AUTHOR = {
|
|
75
|
+
name: 'Orderful',
|
|
76
|
+
url: 'https://github.com/orderful',
|
|
77
|
+
};
|
|
78
|
+
const REPO = 'https://github.com/orderful/droid';
|
|
79
|
+
const REPO_SHORT = 'orderful/droid';
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Get the plugin name with droid- prefix (for namespace isolation).
|
|
83
|
+
* The 'droid' core tool keeps its name as-is to avoid 'droid-droid'.
|
|
84
|
+
*/
|
|
85
|
+
function getPluginName(toolName: string): string {
|
|
86
|
+
if (toolName === 'droid') return 'droid';
|
|
87
|
+
return toolName.startsWith('droid-') ? toolName : `droid-${toolName}`;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function loadToolManifest(toolDir: string): ToolManifest | null {
|
|
91
|
+
const manifestPath = join(toolDir, 'TOOL.yaml');
|
|
92
|
+
if (!existsSync(manifestPath)) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
const content = readFileSync(manifestPath, 'utf-8');
|
|
97
|
+
const parsed = parseYaml(content) as ToolManifest;
|
|
98
|
+
if (!parsed.name || !parsed.description || !parsed.version) {
|
|
99
|
+
console.error(`Invalid TOOL.yaml in ${toolDir}: missing required fields (name, description, version)`);
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
return parsed;
|
|
103
|
+
} catch (err) {
|
|
104
|
+
console.error(`Failed to parse TOOL.yaml in ${toolDir}:`, err);
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function getToolDirs(): string[] {
|
|
110
|
+
const entries = readdirSync(TOOLS_DIR, { withFileTypes: true });
|
|
111
|
+
return entries
|
|
112
|
+
.filter((e) => e.isDirectory())
|
|
113
|
+
.map((e) => join(TOOLS_DIR, e.name))
|
|
114
|
+
.filter((dir) => existsSync(join(dir, 'TOOL.yaml')));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function createPluginJson(manifest: ToolManifest, pluginName: string): PluginJson {
|
|
118
|
+
return {
|
|
119
|
+
name: pluginName,
|
|
120
|
+
version: manifest.version,
|
|
121
|
+
description: manifest.description,
|
|
122
|
+
author: AUTHOR,
|
|
123
|
+
repository: REPO,
|
|
124
|
+
license: 'MIT',
|
|
125
|
+
keywords: [...new Set(['droid', 'ai', manifest.name])],
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function buildPluginManifest(toolDir: string): MarketplacePlugin | null {
|
|
130
|
+
const manifest = loadToolManifest(toolDir);
|
|
131
|
+
if (!manifest) {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const pluginName = getPluginName(manifest.name);
|
|
136
|
+
|
|
137
|
+
console.log(`Generating plugin.json for: ${pluginName} (v${manifest.version})`);
|
|
138
|
+
|
|
139
|
+
// Create .claude-plugin directory in the tool directory
|
|
140
|
+
const claudePluginDir = join(toolDir, '.claude-plugin');
|
|
141
|
+
mkdirSync(claudePluginDir, { recursive: true });
|
|
142
|
+
|
|
143
|
+
// Generate plugin.json
|
|
144
|
+
const pluginJson = createPluginJson(manifest, pluginName);
|
|
145
|
+
writeFileSync(join(claudePluginDir, 'plugin.json'), JSON.stringify(pluginJson, null, 2) + '\n');
|
|
146
|
+
|
|
147
|
+
// Return marketplace entry (path is relative to repo root)
|
|
148
|
+
return {
|
|
149
|
+
name: pluginName,
|
|
150
|
+
description: manifest.description,
|
|
151
|
+
version: manifest.version,
|
|
152
|
+
source: {
|
|
153
|
+
source: 'github',
|
|
154
|
+
repo: REPO_SHORT,
|
|
155
|
+
path: toolDir, // e.g., "src/tools/brain"
|
|
156
|
+
},
|
|
157
|
+
author: {
|
|
158
|
+
name: AUTHOR.name,
|
|
159
|
+
},
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function buildMarketplace(plugins: MarketplacePlugin[]): void {
|
|
164
|
+
// Create marketplace.json at repo root for clean discovery
|
|
165
|
+
const marketplaceDir = '.claude-plugin';
|
|
166
|
+
mkdirSync(marketplaceDir, { recursive: true });
|
|
167
|
+
|
|
168
|
+
const marketplace: MarketplaceJson = {
|
|
169
|
+
name: 'droid',
|
|
170
|
+
description: 'AI-powered development tools for Claude Code',
|
|
171
|
+
owner: AUTHOR,
|
|
172
|
+
plugins: plugins.sort((a, b) => a.name.localeCompare(b.name)),
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
writeFileSync(join(marketplaceDir, 'marketplace.json'), JSON.stringify(marketplace, null, 2) + '\n');
|
|
176
|
+
console.log(`\nMarketplace manifest created at .claude-plugin/marketplace.json with ${plugins.length} plugins`);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function main(): void {
|
|
180
|
+
console.log('Generating Claude Code plugin manifests in src/tools/\n');
|
|
181
|
+
|
|
182
|
+
// Build plugin.json for each tool
|
|
183
|
+
const toolDirs = getToolDirs();
|
|
184
|
+
const plugins: MarketplacePlugin[] = [];
|
|
185
|
+
|
|
186
|
+
for (const toolDir of toolDirs) {
|
|
187
|
+
const plugin = buildPluginManifest(toolDir);
|
|
188
|
+
if (plugin) {
|
|
189
|
+
plugins.push(plugin);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Build marketplace manifest
|
|
194
|
+
buildMarketplace(plugins);
|
|
195
|
+
|
|
196
|
+
console.log('\nPlugin manifest generation complete!');
|
|
197
|
+
console.log(`\nTo test locally:`);
|
|
198
|
+
console.log(` /plugin marketplace add ${process.cwd()}`);
|
|
199
|
+
console.log(` /plugin install droid-brain@droid`);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
try {
|
|
203
|
+
main();
|
|
204
|
+
} catch (err) {
|
|
205
|
+
console.error(err);
|
|
206
|
+
process.exit(1);
|
|
207
|
+
}
|
|
@@ -29,7 +29,9 @@ describe('Badge', () => {
|
|
|
29
29
|
const types = ['skill', 'command', 'agent'] as const;
|
|
30
30
|
for (const type of types) {
|
|
31
31
|
const { lastFrame } = render(<Badge type={type} />);
|
|
32
|
-
expect(lastFrame()).toContain(
|
|
32
|
+
expect(lastFrame()).toContain(
|
|
33
|
+
type.charAt(0).toUpperCase() + type.slice(1),
|
|
34
|
+
);
|
|
33
35
|
}
|
|
34
36
|
});
|
|
35
37
|
});
|
|
@@ -44,7 +46,7 @@ describe('ComponentBadges', () => {
|
|
|
44
46
|
{ name: 'skill1', required: true },
|
|
45
47
|
{ name: 'skill2', required: false },
|
|
46
48
|
],
|
|
47
|
-
commands: ['cmd1'],
|
|
49
|
+
commands: [{ name: 'cmd1', is_alias: false }],
|
|
48
50
|
agents: [],
|
|
49
51
|
},
|
|
50
52
|
};
|
|
@@ -63,7 +65,11 @@ describe('ComponentBadges', () => {
|
|
|
63
65
|
it('renders singular form for single item', () => {
|
|
64
66
|
const singleSkillTool: ToolManifest = {
|
|
65
67
|
...mockTool,
|
|
66
|
-
includes: {
|
|
68
|
+
includes: {
|
|
69
|
+
skills: [{ name: 'skill1', required: true }],
|
|
70
|
+
commands: [],
|
|
71
|
+
agents: [],
|
|
72
|
+
},
|
|
67
73
|
};
|
|
68
74
|
const { lastFrame } = render(<ComponentBadges tool={singleSkillTool} />);
|
|
69
75
|
expect(lastFrame()).toContain('1 skill');
|
|
@@ -82,7 +88,7 @@ describe('ComponentBadges', () => {
|
|
|
82
88
|
...mockTool,
|
|
83
89
|
includes: {
|
|
84
90
|
skills: [{ name: 's1', required: true }],
|
|
85
|
-
commands: ['c1'],
|
|
91
|
+
commands: [{ name: 'c1', is_alias: false }],
|
|
86
92
|
agents: ['a1'],
|
|
87
93
|
},
|
|
88
94
|
};
|
package/src/commands/tui.tsx
CHANGED
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
import { configExists, loadConfig, getAutoUpdateConfig } from '../lib/config';
|
|
15
15
|
import { Platform, type ConfigOption, type ToolManifest } from '../lib/types';
|
|
16
16
|
import { getVersion } from '../lib/version';
|
|
17
|
-
import {
|
|
17
|
+
import { runPackageMigrations } from '../lib/migrations';
|
|
18
18
|
import { type Tab, type View } from './tui/types';
|
|
19
19
|
import { colors, MAX_VISIBLE_ITEMS } from './tui/constants';
|
|
20
20
|
import { TabBar } from './tui/components/TabBar';
|
|
@@ -72,10 +72,10 @@ function App() {
|
|
|
72
72
|
},
|
|
73
73
|
});
|
|
74
74
|
|
|
75
|
-
// Run droid
|
|
75
|
+
// Run droid package migrations on startup (sync installed tools across platforms)
|
|
76
76
|
useEffect(() => {
|
|
77
|
-
const
|
|
78
|
-
|
|
77
|
+
const packageVersion = getVersion();
|
|
78
|
+
runPackageMigrations(packageVersion);
|
|
79
79
|
}, []);
|
|
80
80
|
|
|
81
81
|
// Auto-update app if enabled and update available
|
package/src/lib/migrations.ts
CHANGED
|
@@ -153,6 +153,14 @@ function createPlatformSyncMigration(version: string): Migration {
|
|
|
153
153
|
};
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
+
/**
|
|
157
|
+
* Registry of package-level migrations
|
|
158
|
+
* These run when the @orderful/droid npm package updates
|
|
159
|
+
* Triggered by package version from package.json (e.g., 0.26.0)
|
|
160
|
+
* Run at TUI startup
|
|
161
|
+
*/
|
|
162
|
+
const PACKAGE_MIGRATIONS: Migration[] = [createPlatformSyncMigration('0.25.0')];
|
|
163
|
+
|
|
156
164
|
/**
|
|
157
165
|
* Registry of all tool migrations
|
|
158
166
|
* Key: tool name (e.g., "brain", "comments")
|
|
@@ -169,8 +177,6 @@ const TOOL_MIGRATIONS: Record<string, Migration[]> = {
|
|
|
169
177
|
comments: [createConfigDirMigration('droid-comments', '0.2.6')],
|
|
170
178
|
project: [createConfigDirMigration('droid-project', '0.1.5')],
|
|
171
179
|
coach: [createConfigDirMigration('droid-coach', '0.1.3')],
|
|
172
|
-
// Global migration for the droid meta-tool
|
|
173
|
-
droid: [createPlatformSyncMigration('0.25.0')],
|
|
174
180
|
};
|
|
175
181
|
|
|
176
182
|
/**
|
|
@@ -185,7 +191,18 @@ export function getToolMigrations(toolName: string): Migration[] {
|
|
|
185
191
|
*/
|
|
186
192
|
export function getLastMigratedVersion(toolName: string): string {
|
|
187
193
|
const config = loadConfig();
|
|
188
|
-
|
|
194
|
+
|
|
195
|
+
// Check new location first
|
|
196
|
+
if (config.migrations?.tools?.[toolName]) {
|
|
197
|
+
return config.migrations.tools[toolName];
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Fall back to old location (backward compatibility)
|
|
201
|
+
if (config.migrations && typeof config.migrations[toolName] === 'string') {
|
|
202
|
+
return config.migrations[toolName] as string;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return '0.0.0';
|
|
189
206
|
}
|
|
190
207
|
|
|
191
208
|
/**
|
|
@@ -196,10 +213,22 @@ export function setLastMigratedVersion(
|
|
|
196
213
|
version: string,
|
|
197
214
|
): void {
|
|
198
215
|
const config = loadConfig();
|
|
216
|
+
|
|
217
|
+
// Ensure new structure exists
|
|
199
218
|
if (!config.migrations) {
|
|
200
219
|
config.migrations = {};
|
|
201
220
|
}
|
|
202
|
-
config.migrations
|
|
221
|
+
if (!config.migrations.tools) {
|
|
222
|
+
config.migrations.tools = {};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
config.migrations.tools[toolName] = version;
|
|
226
|
+
|
|
227
|
+
// Clean up old location if it exists
|
|
228
|
+
if (typeof config.migrations[toolName] === 'string') {
|
|
229
|
+
delete config.migrations[toolName];
|
|
230
|
+
}
|
|
231
|
+
|
|
203
232
|
saveConfig(config);
|
|
204
233
|
}
|
|
205
234
|
|
|
@@ -273,3 +302,65 @@ export function runToolMigrations(
|
|
|
273
302
|
|
|
274
303
|
return runMigrations(toolName, lastMigrated, installedVersion);
|
|
275
304
|
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Run package-level migrations
|
|
308
|
+
* Call this on TUI startup with package version from package.json
|
|
309
|
+
*/
|
|
310
|
+
export function runPackageMigrations(packageVersion: string): {
|
|
311
|
+
success: boolean;
|
|
312
|
+
error?: string;
|
|
313
|
+
} {
|
|
314
|
+
const config = loadConfig();
|
|
315
|
+
const lastMigrated = config.migrations?.package ?? '0.0.0';
|
|
316
|
+
|
|
317
|
+
// Only run if the package version is newer than last migrated
|
|
318
|
+
if (compareSemver(packageVersion, lastMigrated) <= 0) {
|
|
319
|
+
return { success: true };
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Filter migrations that need to run
|
|
323
|
+
const pendingMigrations = PACKAGE_MIGRATIONS.filter((m) => {
|
|
324
|
+
const afterFrom = compareSemver(m.version, lastMigrated) > 0;
|
|
325
|
+
const beforeOrAtTo = compareSemver(m.version, packageVersion) <= 0;
|
|
326
|
+
return afterFrom && beforeOrAtTo;
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
if (pendingMigrations.length === 0) {
|
|
330
|
+
// No migrations to run, but still update the version marker
|
|
331
|
+
config.migrations = config.migrations || {};
|
|
332
|
+
config.migrations.package = packageVersion;
|
|
333
|
+
saveConfig(config);
|
|
334
|
+
return { success: true };
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const configDir = getConfigDir();
|
|
338
|
+
|
|
339
|
+
for (const migration of pendingMigrations) {
|
|
340
|
+
try {
|
|
341
|
+
migration.up(configDir);
|
|
342
|
+
logMigration('package', lastMigrated, migration.version, 'OK');
|
|
343
|
+
} catch (error) {
|
|
344
|
+
const errorMessage =
|
|
345
|
+
error instanceof Error ? error.message : String(error);
|
|
346
|
+
logMigration(
|
|
347
|
+
'package',
|
|
348
|
+
lastMigrated,
|
|
349
|
+
migration.version,
|
|
350
|
+
'FAILED',
|
|
351
|
+
errorMessage,
|
|
352
|
+
);
|
|
353
|
+
// Don't update version marker on failure - will retry next time
|
|
354
|
+
return {
|
|
355
|
+
success: false,
|
|
356
|
+
error: `Package migration ${migration.version} failed: ${errorMessage}`,
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// All migrations succeeded, update version marker
|
|
362
|
+
config.migrations = config.migrations || {};
|
|
363
|
+
config.migrations.package = packageVersion;
|
|
364
|
+
saveConfig(config);
|
|
365
|
+
return { success: true };
|
|
366
|
+
}
|
package/src/lib/skill-config.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import inquirer from 'inquirer';
|
|
3
3
|
import { saveSkillOverrides, loadConfig, loadSkillOverrides } from './config';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
ConfigOptionType,
|
|
6
|
+
type SkillOverrides,
|
|
7
|
+
type ConfigOption,
|
|
8
|
+
} from './types';
|
|
5
9
|
|
|
6
10
|
/**
|
|
7
11
|
* Prompt user to configure a skill after install
|
|
@@ -9,12 +13,15 @@ import { ConfigOptionType, type SkillOverrides, type ConfigOption } from './type
|
|
|
9
13
|
export async function promptForSkillConfig(
|
|
10
14
|
skillName: string,
|
|
11
15
|
configSchema: Record<string, ConfigOption>,
|
|
12
|
-
askFirst: boolean = true
|
|
16
|
+
askFirst: boolean = true,
|
|
13
17
|
): Promise<void> {
|
|
14
18
|
const globalConfig = loadConfig();
|
|
19
|
+
let shouldPrompt = true;
|
|
15
20
|
|
|
16
21
|
if (askFirst) {
|
|
17
|
-
const { wantsConfigure } = await inquirer.prompt<{
|
|
22
|
+
const { wantsConfigure } = await inquirer.prompt<{
|
|
23
|
+
wantsConfigure: boolean;
|
|
24
|
+
}>([
|
|
18
25
|
{
|
|
19
26
|
type: 'confirm',
|
|
20
27
|
name: 'wantsConfigure',
|
|
@@ -23,73 +30,104 @@ export async function promptForSkillConfig(
|
|
|
23
30
|
},
|
|
24
31
|
]);
|
|
25
32
|
|
|
26
|
-
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
33
|
+
shouldPrompt = wantsConfigure;
|
|
29
34
|
}
|
|
30
35
|
|
|
31
|
-
console.log(chalk.bold(`\n⚙️ Configure ${skillName}\n`));
|
|
32
|
-
|
|
33
36
|
// Load existing overrides to use as defaults
|
|
34
37
|
const existingOverrides = loadSkillOverrides(skillName);
|
|
35
38
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
};
|
|
39
|
+
let overrides: SkillOverrides = {};
|
|
40
|
+
|
|
41
|
+
if (shouldPrompt) {
|
|
42
|
+
console.log(chalk.bold(`\n⚙️ Configure ${skillName}\n`));
|
|
41
43
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
+
const questions = Object.entries(configSchema).map(([key, option]) => {
|
|
45
|
+
const baseQuestion = {
|
|
46
|
+
name: key,
|
|
47
|
+
message: option.description,
|
|
48
|
+
};
|
|
44
49
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
50
|
+
// Use existing override as default if present
|
|
51
|
+
const existingValue = existingOverrides[key];
|
|
52
|
+
|
|
53
|
+
switch (option.type) {
|
|
54
|
+
case ConfigOptionType.Boolean:
|
|
55
|
+
return {
|
|
56
|
+
...baseQuestion,
|
|
57
|
+
type: 'confirm' as const,
|
|
58
|
+
default: existingValue ?? option.default ?? false,
|
|
59
|
+
};
|
|
60
|
+
case ConfigOptionType.Select:
|
|
61
|
+
return {
|
|
62
|
+
...baseQuestion,
|
|
63
|
+
type: 'list' as const,
|
|
64
|
+
choices: option.options || [],
|
|
65
|
+
default: existingValue ?? option.default,
|
|
66
|
+
};
|
|
67
|
+
case ConfigOptionType.String:
|
|
68
|
+
default: {
|
|
69
|
+
// For user_mention, default to global config value if no existing override
|
|
70
|
+
let defaultValue = existingValue ?? option.default ?? '';
|
|
71
|
+
if (
|
|
72
|
+
key === 'user_mention' &&
|
|
73
|
+
!existingValue &&
|
|
74
|
+
globalConfig.user_mention
|
|
75
|
+
) {
|
|
76
|
+
defaultValue = globalConfig.user_mention;
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
...baseQuestion,
|
|
80
|
+
type: 'input' as const,
|
|
81
|
+
default: defaultValue,
|
|
82
|
+
};
|
|
65
83
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
const answers = await inquirer.prompt(questions);
|
|
88
|
+
|
|
89
|
+
// Save all answers (including defaults)
|
|
90
|
+
for (const [key, value] of Object.entries(answers)) {
|
|
91
|
+
// Save all non-empty values
|
|
92
|
+
if (value !== '' && value !== undefined && value !== null) {
|
|
93
|
+
overrides[key] = value as string | boolean | number;
|
|
71
94
|
}
|
|
72
95
|
}
|
|
73
|
-
}
|
|
96
|
+
} else {
|
|
97
|
+
// User declined to configure - use defaults from schema
|
|
98
|
+
for (const [key, option] of Object.entries(configSchema)) {
|
|
99
|
+
const existingValue = existingOverrides[key];
|
|
100
|
+
let defaultValue =
|
|
101
|
+
existingValue ??
|
|
102
|
+
option.default ??
|
|
103
|
+
(option.type === ConfigOptionType.Boolean ? false : '');
|
|
74
104
|
|
|
75
|
-
|
|
105
|
+
// Special handling for user_mention
|
|
106
|
+
if (
|
|
107
|
+
key === 'user_mention' &&
|
|
108
|
+
!existingValue &&
|
|
109
|
+
globalConfig.user_mention
|
|
110
|
+
) {
|
|
111
|
+
defaultValue = globalConfig.user_mention;
|
|
112
|
+
}
|
|
76
113
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
114
|
+
if (
|
|
115
|
+
defaultValue !== '' &&
|
|
116
|
+
defaultValue !== undefined &&
|
|
117
|
+
defaultValue !== null
|
|
118
|
+
) {
|
|
119
|
+
overrides[key] = defaultValue as string | boolean | number;
|
|
120
|
+
}
|
|
84
121
|
}
|
|
85
122
|
}
|
|
86
123
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
124
|
+
// Always save overrides.yaml when a skill has a config schema
|
|
125
|
+
// Skills expect this file to exist even if all values are defaults
|
|
126
|
+
saveSkillOverrides(skillName, overrides);
|
|
127
|
+
const displayName = skillName.replace(/^droid-/, '');
|
|
128
|
+
console.log(
|
|
129
|
+
chalk.green(
|
|
130
|
+
`\n✓ Configuration saved to ~/.droid/skills/${displayName}/overrides.yaml`,
|
|
131
|
+
),
|
|
132
|
+
);
|
|
95
133
|
}
|