@orderful/droid 0.47.0 → 0.50.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 +1 -1
- package/.claude-plugin/plugin.json +4 -1
- package/CHANGELOG.md +34 -0
- package/bun.lock +137 -3
- package/dist/bin/droid.js +355 -90
- package/dist/commands/pack.d.ts +5 -0
- package/dist/commands/pack.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/lib/pack.d.ts +31 -0
- package/dist/lib/pack.d.ts.map +1 -0
- package/dist/lib/types.d.ts +17 -0
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/tools/brain/.claude-plugin/plugin.json +1 -1
- package/dist/tools/brain/TOOL.yaml +3 -1
- package/dist/tools/brain/skills/brain/SKILL.md +3 -1
- package/dist/tools/brain/skills/brain/references/workflows.md +4 -2
- package/dist/tools/coach/TOOL.yaml +4 -0
- package/dist/tools/code-review/.claude-plugin/plugin.json +3 -2
- package/dist/tools/code-review/TOOL.yaml +4 -1
- package/dist/tools/code-review/agents/codex-context-researcher.md +99 -0
- package/dist/tools/code-review/skills/code-review/SKILL.md +20 -1
- package/dist/tools/codex/.claude-plugin/plugin.json +1 -1
- package/dist/tools/codex/TOOL.yaml +3 -1
- package/dist/tools/codex/skills/codex/SKILL.md +5 -1
- package/dist/tools/codex/skills/codex/scripts/normalize-frontmatter.d.ts +61 -0
- package/dist/tools/codex/skills/codex/scripts/normalize-frontmatter.d.ts.map +1 -0
- package/dist/tools/codex/skills/codex/scripts/normalize-frontmatter.ts +402 -0
- package/dist/tools/comments/.claude-plugin/plugin.json +1 -1
- package/dist/tools/comments/TOOL.yaml +3 -1
- package/dist/tools/comments/skills/comments/SKILL.md +14 -0
- package/dist/tools/droid/.claude-plugin/plugin.json +1 -1
- package/dist/tools/droid/TOOL.yaml +3 -1
- package/dist/tools/droid/skills/droid/SKILL.md +48 -2
- package/dist/tools/droid/skills/droid/references/new-tool-workflow.md +234 -0
- package/dist/tools/edi-schema/TOOL.yaml +2 -0
- package/dist/tools/excalidraw/.claude-plugin/plugin.json +22 -0
- package/dist/tools/excalidraw/TOOL.yaml +18 -0
- package/dist/tools/excalidraw/commands/excalidraw.md +34 -0
- package/dist/tools/excalidraw/skills/excalidraw/SKILL.md +100 -0
- package/dist/tools/excalidraw/skills/excalidraw/references/element-templates.md +336 -0
- package/dist/tools/excalidraw/skills/excalidraw/references/format-spec.md +102 -0
- package/dist/tools/meeting/TOOL.yaml +2 -0
- package/dist/tools/pii/TOOL.yaml +2 -0
- package/dist/tools/plan/.claude-plugin/plugin.json +1 -1
- package/dist/tools/plan/TOOL.yaml +5 -1
- package/dist/tools/plan/skills/plan/SKILL.md +2 -0
- package/dist/tools/plan/skills/plan/references/workflows.md +11 -2
- package/dist/tools/project/TOOL.yaml +2 -0
- package/dist/tools/release/TOOL.yaml +2 -0
- package/dist/tools/share/TOOL.yaml +2 -0
- package/dist/tools/status-update/TOOL.yaml +4 -0
- package/dist/tools/tech-design/TOOL.yaml +2 -0
- package/dist/tools/wrapup/TOOL.yaml +2 -0
- package/package.json +3 -1
- package/scripts/build.ts +3 -2
- package/src/bin/droid.ts +9 -0
- package/src/commands/pack.ts +77 -0
- package/src/lib/pack.test.ts +85 -0
- package/src/lib/pack.ts +293 -0
- package/src/lib/types.ts +19 -0
- package/src/tools/brain/.claude-plugin/plugin.json +1 -1
- package/src/tools/brain/TOOL.yaml +3 -1
- package/src/tools/brain/skills/brain/SKILL.md +3 -1
- package/src/tools/brain/skills/brain/references/workflows.md +4 -2
- package/src/tools/coach/TOOL.yaml +4 -0
- package/src/tools/code-review/.claude-plugin/plugin.json +3 -2
- package/src/tools/code-review/TOOL.yaml +4 -1
- package/src/tools/code-review/agents/codex-context-researcher.md +99 -0
- package/src/tools/code-review/skills/code-review/SKILL.md +20 -1
- package/src/tools/codex/.claude-plugin/plugin.json +1 -1
- package/src/tools/codex/TOOL.yaml +3 -1
- package/src/tools/codex/skills/codex/SKILL.md +5 -1
- package/src/tools/codex/skills/codex/scripts/normalize-frontmatter.test.ts +331 -0
- package/src/tools/codex/skills/codex/scripts/normalize-frontmatter.ts +402 -0
- package/src/tools/comments/.claude-plugin/plugin.json +1 -1
- package/src/tools/comments/TOOL.yaml +3 -1
- package/src/tools/comments/skills/comments/SKILL.md +14 -0
- package/src/tools/droid/.claude-plugin/plugin.json +1 -1
- package/src/tools/droid/TOOL.yaml +3 -1
- package/src/tools/droid/skills/droid/SKILL.md +48 -2
- package/src/tools/droid/skills/droid/references/new-tool-workflow.md +234 -0
- package/src/tools/edi-schema/TOOL.yaml +2 -0
- package/src/tools/excalidraw/.claude-plugin/plugin.json +22 -0
- package/src/tools/excalidraw/TOOL.yaml +18 -0
- package/src/tools/excalidraw/commands/excalidraw.md +34 -0
- package/src/tools/excalidraw/skills/excalidraw/SKILL.md +100 -0
- package/src/tools/excalidraw/skills/excalidraw/references/element-templates.md +336 -0
- package/src/tools/excalidraw/skills/excalidraw/references/format-spec.md +102 -0
- package/src/tools/meeting/TOOL.yaml +2 -0
- package/src/tools/pii/TOOL.yaml +2 -0
- package/src/tools/plan/.claude-plugin/plugin.json +1 -1
- package/src/tools/plan/TOOL.yaml +5 -1
- package/src/tools/plan/skills/plan/SKILL.md +2 -0
- package/src/tools/plan/skills/plan/references/workflows.md +11 -2
- package/src/tools/project/TOOL.yaml +2 -0
- package/src/tools/release/TOOL.yaml +2 -0
- package/src/tools/share/TOOL.yaml +2 -0
- package/src/tools/status-update/TOOL.yaml +4 -0
- package/src/tools/tech-design/TOOL.yaml +2 -0
- package/src/tools/wrapup/TOOL.yaml +2 -0
- package/dist/tools/codex/skills/codex/scripts/git-scripts.test.ts +0 -364
- package/dist/tools/pii/skills/pii/scripts/presidio.test.ts +0 -444
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { ToolAudience } from '../lib/types';
|
|
3
|
+
import { getAudienceInfo, buildPack } from '../lib/pack';
|
|
4
|
+
|
|
5
|
+
function isValidAudience(value: string): value is ToolAudience {
|
|
6
|
+
return Object.values(ToolAudience).includes(value as ToolAudience);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export async function packCommand(
|
|
10
|
+
audience: string | undefined,
|
|
11
|
+
options: { list?: boolean; output?: string },
|
|
12
|
+
): Promise<void> {
|
|
13
|
+
// --list mode: show audiences and tool counts
|
|
14
|
+
if (options.list) {
|
|
15
|
+
const infos = getAudienceInfo();
|
|
16
|
+
|
|
17
|
+
if (infos.length === 0) {
|
|
18
|
+
console.log(chalk.yellow('No tools with audience metadata found.'));
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
console.log(chalk.bold('\nAvailable audiences:\n'));
|
|
23
|
+
for (const info of infos) {
|
|
24
|
+
console.log(
|
|
25
|
+
` ${chalk.cyan(info.audience.padEnd(24))} ${chalk.gray(`${info.toolCount} tools`)} ${chalk.gray(info.toolNames.join(', '))}`,
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
console.log('');
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Require audience argument
|
|
33
|
+
if (!audience) {
|
|
34
|
+
console.error(chalk.red('\nError: audience argument required'));
|
|
35
|
+
console.log(chalk.gray('Usage: droid pack <audience>'));
|
|
36
|
+
console.log(chalk.gray(' droid pack --list'));
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Validate audience
|
|
41
|
+
if (!isValidAudience(audience)) {
|
|
42
|
+
console.error(chalk.red(`\nError: unknown audience '${audience}'`));
|
|
43
|
+
console.log(chalk.gray('\nValid audiences:'));
|
|
44
|
+
for (const a of Object.values(ToolAudience)) {
|
|
45
|
+
if (a !== ToolAudience.All) {
|
|
46
|
+
console.log(chalk.gray(` - ${a}`));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const outputDir = options.output || process.cwd();
|
|
53
|
+
|
|
54
|
+
console.log(
|
|
55
|
+
chalk.bold(`\nPacking tools for ${chalk.cyan(audience)}...`),
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
const result = await buildPack({ audience, outputDir });
|
|
59
|
+
|
|
60
|
+
if (!result.success) {
|
|
61
|
+
console.error(chalk.red(`\n${result.message}`));
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
console.log(chalk.green(`\n${result.message}`));
|
|
66
|
+
console.log(chalk.gray(` ${result.toolCount} tools included`));
|
|
67
|
+
console.log(chalk.gray(` Output: ${result.outputPath}`));
|
|
68
|
+
|
|
69
|
+
if (result.warnings && result.warnings.length > 0) {
|
|
70
|
+
console.log(chalk.yellow('\nWarnings:'));
|
|
71
|
+
for (const warning of result.warnings) {
|
|
72
|
+
console.log(chalk.yellow(` - ${warning}`));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
console.log('');
|
|
77
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { describe, it, expect } from 'bun:test';
|
|
2
|
+
import { ToolAudience } from './types';
|
|
3
|
+
import { getToolsForAudience, getAudienceInfo } from './pack';
|
|
4
|
+
|
|
5
|
+
// Uses real bundled tools for integration-style tests
|
|
6
|
+
|
|
7
|
+
describe('getToolsForAudience', () => {
|
|
8
|
+
it('should include "all" audience tools in any audience pack', () => {
|
|
9
|
+
const tools = getToolsForAudience(ToolAudience.Engineering);
|
|
10
|
+
const toolNames = tools.map((t) => t.name);
|
|
11
|
+
// brain is audience: [all]
|
|
12
|
+
expect(toolNames).toContain('brain');
|
|
13
|
+
// comments is audience: [all]
|
|
14
|
+
expect(toolNames).toContain('comments');
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should include audience-specific tools', () => {
|
|
18
|
+
const tools = getToolsForAudience(ToolAudience.Engineering);
|
|
19
|
+
const toolNames = tools.map((t) => t.name);
|
|
20
|
+
// code-review is audience: [engineering]
|
|
21
|
+
expect(toolNames).toContain('code-review');
|
|
22
|
+
// release is audience: [engineering]
|
|
23
|
+
expect(toolNames).toContain('release');
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('should include tools with prerequisites (codex)', () => {
|
|
27
|
+
const tools = getToolsForAudience(ToolAudience.Engineering);
|
|
28
|
+
const toolNames = tools.map((t) => t.name);
|
|
29
|
+
expect(toolNames).toContain('codex');
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should not include tools from other audiences', () => {
|
|
33
|
+
// edi-schema is engineering-only
|
|
34
|
+
const tools = getToolsForAudience(ToolAudience.Product);
|
|
35
|
+
const toolNames = tools.map((t) => t.name);
|
|
36
|
+
expect(toolNames).not.toContain('edi-schema');
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should return tools for audiences with only "all" tools', () => {
|
|
40
|
+
const tools = getToolsForAudience(ToolAudience.CustomerSupport);
|
|
41
|
+
// customer-support has no specific tools but gets all 'all' audience tools
|
|
42
|
+
expect(tools.length).toBeGreaterThan(0);
|
|
43
|
+
// Every tool should have audience 'all'
|
|
44
|
+
for (const tool of tools) {
|
|
45
|
+
expect(tool.audience).toContain(ToolAudience.All);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should skip tools without audience metadata', () => {
|
|
50
|
+
const tools = getToolsForAudience(ToolAudience.Engineering);
|
|
51
|
+
for (const tool of tools) {
|
|
52
|
+
expect(tool.audience).toBeDefined();
|
|
53
|
+
expect(tool.audience!.length).toBeGreaterThan(0);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe('getAudienceInfo', () => {
|
|
59
|
+
it('should return info for audiences that have tools', () => {
|
|
60
|
+
const infos = getAudienceInfo();
|
|
61
|
+
expect(infos.length).toBeGreaterThan(0);
|
|
62
|
+
|
|
63
|
+
// Engineering should be present (has edi-schema, code-review, etc.)
|
|
64
|
+
const engineering = infos.find(
|
|
65
|
+
(i) => i.audience === ToolAudience.Engineering,
|
|
66
|
+
);
|
|
67
|
+
expect(engineering).toBeDefined();
|
|
68
|
+
expect(engineering!.toolCount).toBeGreaterThan(0);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('should not include the "all" audience itself', () => {
|
|
72
|
+
const infos = getAudienceInfo();
|
|
73
|
+
const all = infos.find((i) => i.audience === ToolAudience.All);
|
|
74
|
+
expect(all).toBeUndefined();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should count "all" audience tools in each audience', () => {
|
|
78
|
+
const infos = getAudienceInfo();
|
|
79
|
+
const engineering = infos.find(
|
|
80
|
+
(i) => i.audience === ToolAudience.Engineering,
|
|
81
|
+
);
|
|
82
|
+
// Engineering should include 'all' tools like brain, comments
|
|
83
|
+
expect(engineering!.toolNames).toContain('brain');
|
|
84
|
+
});
|
|
85
|
+
});
|
package/src/lib/pack.ts
ADDED
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync, createWriteStream } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import archiver from 'archiver';
|
|
4
|
+
import { getBundledTools, getBundledToolsDir } from './tools';
|
|
5
|
+
import { ToolAudience, type ToolManifest } from './types';
|
|
6
|
+
|
|
7
|
+
export interface AudienceInfo {
|
|
8
|
+
audience: ToolAudience;
|
|
9
|
+
toolCount: number;
|
|
10
|
+
toolNames: string[];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface PackOptions {
|
|
14
|
+
audience: ToolAudience;
|
|
15
|
+
outputDir: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface PackResult {
|
|
19
|
+
success: boolean;
|
|
20
|
+
message: string;
|
|
21
|
+
outputPath?: string;
|
|
22
|
+
toolCount?: number;
|
|
23
|
+
warnings?: string[];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Get tools filtered by audience
|
|
28
|
+
* Tools with audience 'all' are always included
|
|
29
|
+
*/
|
|
30
|
+
export function getToolsForAudience(
|
|
31
|
+
audience: ToolAudience,
|
|
32
|
+
): ToolManifest[] {
|
|
33
|
+
const allTools = getBundledTools();
|
|
34
|
+
|
|
35
|
+
return allTools.filter((tool) => {
|
|
36
|
+
// Skip tools without audience metadata
|
|
37
|
+
if (!tool.audience || tool.audience.length === 0) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Must match the requested audience OR be audience 'all'
|
|
42
|
+
return (
|
|
43
|
+
tool.audience.includes(audience) ||
|
|
44
|
+
tool.audience.includes(ToolAudience.All)
|
|
45
|
+
);
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Get audience info for all audiences that have tools
|
|
51
|
+
*/
|
|
52
|
+
export function getAudienceInfo(): AudienceInfo[] {
|
|
53
|
+
const allTools = getBundledTools();
|
|
54
|
+
const audiences = Object.values(ToolAudience).filter(
|
|
55
|
+
(a) => a !== ToolAudience.All,
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
return audiences
|
|
59
|
+
.map((audience) => {
|
|
60
|
+
const tools = allTools.filter(
|
|
61
|
+
(tool) =>
|
|
62
|
+
tool.audience &&
|
|
63
|
+
(tool.audience.includes(audience) ||
|
|
64
|
+
tool.audience.includes(ToolAudience.All)),
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
audience,
|
|
69
|
+
toolCount: tools.length,
|
|
70
|
+
toolNames: tools.map((t) => t.name),
|
|
71
|
+
};
|
|
72
|
+
})
|
|
73
|
+
.filter((info) => info.toolCount > 0);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Collect all artifact paths for a tool (skills, commands, agents)
|
|
78
|
+
* Returns paths relative to the zip root (installed format)
|
|
79
|
+
*/
|
|
80
|
+
function collectToolArtifacts(
|
|
81
|
+
tool: ToolManifest,
|
|
82
|
+
): Array<{ sourcePath: string; zipPath: string }> {
|
|
83
|
+
const artifacts: Array<{ sourcePath: string; zipPath: string }> = [];
|
|
84
|
+
const toolDir = join(getBundledToolsDir(), tool.name);
|
|
85
|
+
|
|
86
|
+
// Skills
|
|
87
|
+
for (const skill of tool.includes.skills) {
|
|
88
|
+
const skillDir = join(toolDir, 'skills', skill.name);
|
|
89
|
+
if (!existsSync(skillDir)) continue;
|
|
90
|
+
|
|
91
|
+
// SKILL.md
|
|
92
|
+
const skillMd = join(skillDir, 'SKILL.md');
|
|
93
|
+
if (existsSync(skillMd)) {
|
|
94
|
+
artifacts.push({
|
|
95
|
+
sourcePath: skillMd,
|
|
96
|
+
zipPath: `skills/${skill.name}/SKILL.md`,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// references/
|
|
101
|
+
const refsDir = join(skillDir, 'references');
|
|
102
|
+
if (existsSync(refsDir)) {
|
|
103
|
+
const refFiles = readdirSync(refsDir).filter((f) => f.endsWith('.md'));
|
|
104
|
+
for (const file of refFiles) {
|
|
105
|
+
artifacts.push({
|
|
106
|
+
sourcePath: join(refsDir, file),
|
|
107
|
+
zipPath: `skills/${skill.name}/references/${file}`,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Commands
|
|
114
|
+
const commandsDir = join(toolDir, 'commands');
|
|
115
|
+
if (existsSync(commandsDir)) {
|
|
116
|
+
const commandFiles = readdirSync(commandsDir).filter(
|
|
117
|
+
(f) => f.endsWith('.md') && f.toLowerCase() !== 'readme.md',
|
|
118
|
+
);
|
|
119
|
+
for (const file of commandFiles) {
|
|
120
|
+
artifacts.push({
|
|
121
|
+
sourcePath: join(commandsDir, file),
|
|
122
|
+
zipPath: `commands/${file}`,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Agents
|
|
128
|
+
const agentsDir = join(toolDir, 'agents');
|
|
129
|
+
if (existsSync(agentsDir)) {
|
|
130
|
+
const agentFiles = readdirSync(agentsDir).filter(
|
|
131
|
+
(f) => f.endsWith('.md') && f.toLowerCase() !== 'readme.md',
|
|
132
|
+
);
|
|
133
|
+
for (const file of agentFiles) {
|
|
134
|
+
artifacts.push({
|
|
135
|
+
sourcePath: join(agentsDir, file),
|
|
136
|
+
zipPath: `agents/${file}`,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return artifacts;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Generate CLAUDE.md content with skill registration links
|
|
146
|
+
*/
|
|
147
|
+
function generateClaudeMd(tools: ToolManifest[]): string {
|
|
148
|
+
const skillNames: string[] = [];
|
|
149
|
+
for (const tool of tools) {
|
|
150
|
+
for (const skill of tool.includes.skills) {
|
|
151
|
+
skillNames.push(skill.name);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const lines = [
|
|
156
|
+
'# Droid Skills',
|
|
157
|
+
'',
|
|
158
|
+
'Skills installed from a Droid pack. Each skill teaches your AI new capabilities.',
|
|
159
|
+
'',
|
|
160
|
+
];
|
|
161
|
+
|
|
162
|
+
for (const name of skillNames) {
|
|
163
|
+
lines.push(`- [${name}](skills/${name}/SKILL.md)`);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
lines.push('');
|
|
167
|
+
return lines.join('\n');
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Generate README.md with install instructions
|
|
172
|
+
*/
|
|
173
|
+
function generateReadme(audience: ToolAudience, tools: ToolManifest[]): string {
|
|
174
|
+
const lines = [
|
|
175
|
+
`# Droid Pack: ${audience}`,
|
|
176
|
+
'',
|
|
177
|
+
'This pack contains AI skills, commands, and agents for Claude Desktop.',
|
|
178
|
+
'',
|
|
179
|
+
'## Installation',
|
|
180
|
+
'',
|
|
181
|
+
'1. Unzip this archive',
|
|
182
|
+
'2. Copy the contents into your `~/.claude/` directory:',
|
|
183
|
+
'',
|
|
184
|
+
'```bash',
|
|
185
|
+
'# From the directory where you unzipped:',
|
|
186
|
+
'cp -r skills/ ~/.claude/skills/',
|
|
187
|
+
'cp -r commands/ ~/.claude/commands/',
|
|
188
|
+
'cp -r agents/ ~/.claude/agents/',
|
|
189
|
+
'```',
|
|
190
|
+
'',
|
|
191
|
+
'3. Append the contents of `CLAUDE.md` to your `~/.claude/CLAUDE.md`:',
|
|
192
|
+
'',
|
|
193
|
+
'```bash',
|
|
194
|
+
'cat CLAUDE.md >> ~/.claude/CLAUDE.md',
|
|
195
|
+
'```',
|
|
196
|
+
'',
|
|
197
|
+
'## Included Tools',
|
|
198
|
+
'',
|
|
199
|
+
];
|
|
200
|
+
|
|
201
|
+
for (const tool of tools) {
|
|
202
|
+
lines.push(`- **${tool.name}** (v${tool.version}) — ${tool.description}`);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
lines.push('');
|
|
206
|
+
return lines.join('\n');
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Check for missing dependencies and return warnings
|
|
211
|
+
*/
|
|
212
|
+
function checkDependencies(tools: ToolManifest[]): string[] {
|
|
213
|
+
const warnings: string[] = [];
|
|
214
|
+
const toolNames = new Set(tools.map((t) => t.name));
|
|
215
|
+
|
|
216
|
+
for (const tool of tools) {
|
|
217
|
+
if (!tool.dependencies) continue;
|
|
218
|
+
for (const dep of tool.dependencies) {
|
|
219
|
+
if (!toolNames.has(dep)) {
|
|
220
|
+
warnings.push(
|
|
221
|
+
`Tool '${tool.name}' depends on '${dep}' which is not included in this pack`,
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return warnings;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Build a zip pack for the given audience
|
|
232
|
+
*/
|
|
233
|
+
export async function buildPack(options: PackOptions): Promise<PackResult> {
|
|
234
|
+
const { audience, outputDir } = options;
|
|
235
|
+
|
|
236
|
+
// Get filtered tools
|
|
237
|
+
const tools = getToolsForAudience(audience);
|
|
238
|
+
|
|
239
|
+
if (tools.length === 0) {
|
|
240
|
+
return {
|
|
241
|
+
success: false,
|
|
242
|
+
message: `No tools found for audience '${audience}'`,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Check dependencies
|
|
247
|
+
const warnings = checkDependencies(tools);
|
|
248
|
+
|
|
249
|
+
// Build the zip
|
|
250
|
+
const filename = `droid-${audience}-pack.zip`;
|
|
251
|
+
const outputPath = join(outputDir, filename);
|
|
252
|
+
|
|
253
|
+
return new Promise((resolve) => {
|
|
254
|
+
const output = createWriteStream(outputPath);
|
|
255
|
+
const archive = archiver('zip', { zlib: { level: 9 } });
|
|
256
|
+
|
|
257
|
+
output.on('close', () => {
|
|
258
|
+
resolve({
|
|
259
|
+
success: true,
|
|
260
|
+
message: `Pack created: ${filename}`,
|
|
261
|
+
outputPath,
|
|
262
|
+
toolCount: tools.length,
|
|
263
|
+
warnings: warnings.length > 0 ? warnings : undefined,
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
archive.on('error', (err: Error) => {
|
|
268
|
+
resolve({
|
|
269
|
+
success: false,
|
|
270
|
+
message: `Failed to create pack: ${err.message}`,
|
|
271
|
+
});
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
archive.pipe(output);
|
|
275
|
+
|
|
276
|
+
// Add all tool artifacts
|
|
277
|
+
for (const tool of tools) {
|
|
278
|
+
const artifacts = collectToolArtifacts(tool);
|
|
279
|
+
for (const artifact of artifacts) {
|
|
280
|
+
const content = readFileSync(artifact.sourcePath, 'utf-8');
|
|
281
|
+
archive.append(content, { name: artifact.zipPath });
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Add generated CLAUDE.md
|
|
286
|
+
archive.append(generateClaudeMd(tools), { name: 'CLAUDE.md' });
|
|
287
|
+
|
|
288
|
+
// Add generated README.md
|
|
289
|
+
archive.append(generateReadme(audience, tools), { name: 'README.md' });
|
|
290
|
+
|
|
291
|
+
archive.finalize();
|
|
292
|
+
});
|
|
293
|
+
}
|
package/src/lib/types.ts
CHANGED
|
@@ -219,12 +219,31 @@ export interface ToolIncludes {
|
|
|
219
219
|
agents: string[];
|
|
220
220
|
}
|
|
221
221
|
|
|
222
|
+
export enum ToolAudience {
|
|
223
|
+
All = 'all',
|
|
224
|
+
Engineering = 'engineering',
|
|
225
|
+
Product = 'product',
|
|
226
|
+
Design = 'design',
|
|
227
|
+
IntegrationDevelopers = 'integration-developers',
|
|
228
|
+
CustomerSupport = 'customer-support',
|
|
229
|
+
NetworkOperations = 'network-operations',
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export interface ToolPrerequisite {
|
|
233
|
+
name: string;
|
|
234
|
+
description: string;
|
|
235
|
+
check: string;
|
|
236
|
+
install_hint: string;
|
|
237
|
+
}
|
|
238
|
+
|
|
222
239
|
export interface ToolManifest {
|
|
223
240
|
name: string;
|
|
224
241
|
description: string;
|
|
225
242
|
version: string;
|
|
226
243
|
status?: SkillStatus;
|
|
244
|
+
audience?: ToolAudience[];
|
|
227
245
|
includes: ToolIncludes;
|
|
228
246
|
dependencies?: string[];
|
|
247
|
+
prerequisites?: ToolPrerequisite[];
|
|
229
248
|
config_schema?: Record<string, ConfigOption>;
|
|
230
249
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "droid-brain",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"description": "Your scratchpad (or brain) - a collaborative space for planning and research. Create docs with /brain plan, /brain research, or /brain review. Use @mentions for async discussion. Docs persist across sessions.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Orderful",
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
name: brain
|
|
2
2
|
description: "Your scratchpad (or brain) - a collaborative space for planning and research. Create docs with /brain plan, /brain research, or /brain review. Use @mentions for async discussion. Docs persist across sessions."
|
|
3
|
-
version: 0.4.
|
|
3
|
+
version: 0.4.2
|
|
4
4
|
status: beta
|
|
5
|
+
audience:
|
|
6
|
+
- all
|
|
5
7
|
|
|
6
8
|
includes:
|
|
7
9
|
skills:
|
|
@@ -121,10 +121,12 @@ Full procedure: `references/workflows.md` § Adding
|
|
|
121
121
|
|
|
122
122
|
## Checking Comments
|
|
123
123
|
|
|
124
|
-
**Trigger:** `/brain check`
|
|
124
|
+
**Trigger:** `/brain check` or `/brain check --holistic`
|
|
125
125
|
|
|
126
126
|
Find `> @droid` comments in active doc and address each. Preserve original comment, add response below with `> @{user_mention}`.
|
|
127
127
|
|
|
128
|
+
With `--holistic`: group related comments into thematic clusters and respond cohesively to each cluster instead of one-by-one. Unrelated comments still get individual responses. See `comments` skill for full holistic mode behaviour.
|
|
129
|
+
|
|
128
130
|
Full procedure: `references/workflows.md` § Checking
|
|
129
131
|
|
|
130
132
|
## Cleaning Up Resolved Threads
|
|
@@ -150,9 +150,11 @@ Detailed procedures for each brain operation.
|
|
|
150
150
|
|
|
151
151
|
## Checking
|
|
152
152
|
|
|
153
|
-
**Trigger:** `/brain check`
|
|
153
|
+
**Trigger:** `/brain check` or `/brain check --holistic`
|
|
154
154
|
|
|
155
|
-
**Prefer `comments` skill:** If the `comments` skill is installed, use `/comments check` instead - it provides better comment detection, cleanup workflows, and supports the full `@droid`/`@user` convention. The workflow below is a fallback for basic comment handling.
|
|
155
|
+
**Prefer `comments` skill:** If the `comments` skill is installed, use `/comments check` (or `/comments check --holistic`) instead - it provides better comment detection, cleanup workflows, and supports the full `@droid`/`@user` convention. The workflow below is a fallback for basic comment handling.
|
|
156
|
+
|
|
157
|
+
**Holistic mode (`--holistic`):** Pass the flag through to the comments skill. If using the fallback workflow below, apply the same principle: after step 4 (finding all comments), group related comments into thematic clusters and respond cohesively to each cluster at the last comment in the group. Unrelated comments get individual responses as normal.
|
|
156
158
|
|
|
157
159
|
**Directionality:** The @mention is the **target**, not the author:
|
|
158
160
|
|
|
@@ -2,6 +2,10 @@ name: coach
|
|
|
2
2
|
description: "Learning-mode AI assistance - AI as coach, not crutch. Use /coach plan for co-authored planning, /coach scaffold for structure with hints, /coach review for Socratic questions."
|
|
3
3
|
version: 0.3.0
|
|
4
4
|
status: beta
|
|
5
|
+
audience:
|
|
6
|
+
- engineering
|
|
7
|
+
- product
|
|
8
|
+
- design
|
|
5
9
|
|
|
6
10
|
includes:
|
|
7
11
|
skills:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "droid-code-review",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Comprehensive code review using specialized agents. Reviews PRs, staged changes, branches, or specific files with confidence scoring.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Orderful",
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
"./agents/edi-standards-reviewer.md",
|
|
24
24
|
"./agents/error-handling-reviewer.md",
|
|
25
25
|
"./agents/test-coverage-analyzer.md",
|
|
26
|
-
"./agents/type-reviewer.md"
|
|
26
|
+
"./agents/type-reviewer.md",
|
|
27
|
+
"./agents/codex-context-researcher.md"
|
|
27
28
|
]
|
|
28
29
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
name: code-review
|
|
2
2
|
description: "Comprehensive code review using specialized agents. Reviews PRs, staged changes, branches, or specific files with confidence scoring."
|
|
3
|
-
version: 0.
|
|
3
|
+
version: 0.3.0
|
|
4
4
|
status: alpha
|
|
5
|
+
audience:
|
|
6
|
+
- engineering
|
|
5
7
|
|
|
6
8
|
includes:
|
|
7
9
|
skills:
|
|
@@ -15,5 +17,6 @@ includes:
|
|
|
15
17
|
- error-handling-reviewer
|
|
16
18
|
- test-coverage-analyzer
|
|
17
19
|
- type-reviewer
|
|
20
|
+
- codex-context-researcher
|
|
18
21
|
|
|
19
22
|
dependencies: []
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: codex-context-researcher
|
|
3
|
+
description: "Search codex for domain knowledge relevant to code changes. Enriches code review findings with organisational context from PRDs, tech designs, and architectural decisions."
|
|
4
|
+
tools:
|
|
5
|
+
- Read
|
|
6
|
+
- Grep
|
|
7
|
+
- Glob
|
|
8
|
+
- Bash
|
|
9
|
+
color: cyan
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
You are a codex research agent that finds organisational domain knowledge relevant to a set of code changes. Your output enriches code review findings — you do NOT review code yourself.
|
|
13
|
+
|
|
14
|
+
## Inputs
|
|
15
|
+
|
|
16
|
+
You will receive:
|
|
17
|
+
|
|
18
|
+
- A list of changed file paths
|
|
19
|
+
- A brief summary of the diff (module names, domain concepts)
|
|
20
|
+
- Optionally, custom instructions from the user
|
|
21
|
+
|
|
22
|
+
## Procedure
|
|
23
|
+
|
|
24
|
+
### Step 1: Check Codex Availability
|
|
25
|
+
|
|
26
|
+
Run: `droid config --get tools.codex`
|
|
27
|
+
|
|
28
|
+
Parse the output for `codex_repo`. If not set or the command fails, return:
|
|
29
|
+
|
|
30
|
+
```json
|
|
31
|
+
{
|
|
32
|
+
"status": "not_configured",
|
|
33
|
+
"entries_searched": 0,
|
|
34
|
+
"entries_matched": 0,
|
|
35
|
+
"domain_context": "",
|
|
36
|
+
"sources": []
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Step 2: Read Index
|
|
41
|
+
|
|
42
|
+
Read `{codex_repo}/index.yaml` — this is the codex inventory. It lists every entry by category (projects, patterns, topics, domains, proposals) with names, titles, and aliases.
|
|
43
|
+
|
|
44
|
+
If the index doesn't exist or is empty, return `status: "not_configured"`.
|
|
45
|
+
|
|
46
|
+
### Step 3: Extract Search Terms
|
|
47
|
+
|
|
48
|
+
From the changed file paths, extract terms to match against the index:
|
|
49
|
+
|
|
50
|
+
- **Module/service names** from paths (e.g., `transactionTemplate` from `src/modules/transactionTemplate/`)
|
|
51
|
+
- **Directory segments** that suggest feature areas (e.g., `billing`, `partnership`, `webhook`)
|
|
52
|
+
|
|
53
|
+
Do NOT hardcode domain concepts — let the index be the source of truth for what knowledge exists.
|
|
54
|
+
|
|
55
|
+
### Step 4: Match Against Index
|
|
56
|
+
|
|
57
|
+
Match extracted terms against index entry names, titles, and aliases across all categories. Select up to **5** most relevant entries. Prioritise:
|
|
58
|
+
|
|
59
|
+
1. Exact name/alias matches
|
|
60
|
+
2. Partial matches on entry titles
|
|
61
|
+
3. Related entries where extracted terms appear in the name or title
|
|
62
|
+
|
|
63
|
+
If no entries match, return `status: "no_relevant_entries"`.
|
|
64
|
+
|
|
65
|
+
### Step 5: Read and Extract
|
|
66
|
+
|
|
67
|
+
For each matched entry, read the full content. Extract **only**:
|
|
68
|
+
|
|
69
|
+
- Key patterns and conventions
|
|
70
|
+
- Architectural decisions and their rationale
|
|
71
|
+
- Gotchas, constraints, and explicit rules
|
|
72
|
+
- Domain-specific validation requirements
|
|
73
|
+
|
|
74
|
+
**Skip** generic content, background sections, and anything not actionable for a code reviewer.
|
|
75
|
+
|
|
76
|
+
### Step 6: Return Structured Output
|
|
77
|
+
|
|
78
|
+
Return a JSON block:
|
|
79
|
+
|
|
80
|
+
```json
|
|
81
|
+
{
|
|
82
|
+
"status": "ok",
|
|
83
|
+
"entries_searched": 12,
|
|
84
|
+
"entries_matched": 3,
|
|
85
|
+
"domain_context": "Bullet points of reviewer-actionable domain knowledge (~300 words max)",
|
|
86
|
+
"sources": ["entry-name-1", "entry-name-2"]
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
- `entries_searched`: total entries in index
|
|
91
|
+
- `entries_matched`: entries that matched search terms
|
|
92
|
+
- `domain_context`: concise, reviewer-actionable bullet points. Focus on what a reviewer should look for or verify. Max ~300 words
|
|
93
|
+
- `sources`: entry names used (for citation in the review report)
|
|
94
|
+
|
|
95
|
+
## Important
|
|
96
|
+
|
|
97
|
+
- Do NOT review code. Your job is context retrieval only.
|
|
98
|
+
- Keep `domain_context` focused and actionable — reviewers will read this alongside their own findings.
|
|
99
|
+
- If codex isn't configured or has no relevant entries, return early with the appropriate status. Don't fabricate context.
|