beth-copilot 1.0.12 → 1.0.14
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/CHANGELOG.md +47 -0
- package/README.md +173 -32
- package/bin/cli.js +111 -193
- package/dist/cli/commands/doctor.d.ts +25 -0
- package/dist/cli/commands/doctor.d.ts.map +1 -0
- package/dist/cli/commands/doctor.js +238 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/doctor.test.d.ts +6 -0
- package/dist/cli/commands/doctor.test.d.ts.map +1 -0
- package/dist/cli/commands/doctor.test.js +137 -0
- package/dist/cli/commands/doctor.test.js.map +1 -0
- package/dist/cli/commands/index.d.ts +7 -0
- package/dist/cli/commands/index.d.ts.map +1 -0
- package/dist/cli/commands/index.js +10 -0
- package/dist/cli/commands/index.js.map +1 -0
- package/dist/cli/commands/quickstart.d.ts +18 -0
- package/dist/cli/commands/quickstart.d.ts.map +1 -0
- package/dist/cli/commands/quickstart.js +141 -0
- package/dist/cli/commands/quickstart.js.map +1 -0
- package/dist/core/agents/index.d.ts +6 -0
- package/dist/core/agents/index.d.ts.map +1 -0
- package/dist/core/agents/index.js +6 -0
- package/dist/core/agents/index.js.map +1 -0
- package/dist/core/agents/loader.d.ts +49 -0
- package/dist/core/agents/loader.d.ts.map +1 -0
- package/dist/core/agents/loader.js +217 -0
- package/dist/core/agents/loader.js.map +1 -0
- package/dist/core/agents/loader.test.d.ts +7 -0
- package/dist/core/agents/loader.test.d.ts.map +1 -0
- package/dist/core/agents/loader.test.js +144 -0
- package/dist/core/agents/loader.test.js.map +1 -0
- package/dist/core/agents/types.d.ts +77 -0
- package/dist/core/agents/types.d.ts.map +1 -0
- package/dist/core/agents/types.js +8 -0
- package/dist/core/agents/types.js.map +1 -0
- package/dist/core/agents/types.test.d.ts +6 -0
- package/dist/core/agents/types.test.d.ts.map +1 -0
- package/dist/core/agents/types.test.js +254 -0
- package/dist/core/agents/types.test.js.map +1 -0
- package/dist/core/skills/index.d.ts +6 -0
- package/dist/core/skills/index.d.ts.map +1 -0
- package/dist/core/skills/index.js +6 -0
- package/dist/core/skills/index.js.map +1 -0
- package/dist/core/skills/loader.d.ts +69 -0
- package/dist/core/skills/loader.d.ts.map +1 -0
- package/dist/core/skills/loader.js +243 -0
- package/dist/core/skills/loader.js.map +1 -0
- package/dist/core/skills/loader.test.d.ts +7 -0
- package/dist/core/skills/loader.test.d.ts.map +1 -0
- package/dist/core/skills/loader.test.js +184 -0
- package/dist/core/skills/loader.test.js.map +1 -0
- package/dist/core/skills/types.d.ts +58 -0
- package/dist/core/skills/types.d.ts.map +1 -0
- package/dist/core/skills/types.js +8 -0
- package/dist/core/skills/types.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/index.d.ts +7 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +7 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/pathValidation.d.ts +69 -0
- package/dist/lib/pathValidation.d.ts.map +1 -0
- package/dist/lib/pathValidation.js +185 -0
- package/dist/lib/pathValidation.js.map +1 -0
- package/dist/lib/pathValidation.test.d.ts +9 -0
- package/dist/lib/pathValidation.test.d.ts.map +1 -0
- package/dist/lib/pathValidation.test.js +195 -0
- package/dist/lib/pathValidation.test.js.map +1 -0
- package/package.json +15 -3
- package/templates/.github/agents/developer.agent.md +9 -0
- package/templates/.github/agents/product-manager.agent.md +9 -0
- package/templates/.github/agents/researcher.agent.md +9 -0
- package/templates/.github/agents/security-reviewer.agent.md +9 -2
- package/templates/.github/agents/tester.agent.md +9 -0
- package/templates/.github/agents/ux-designer.agent.md +9 -0
- package/templates/.github/copilot-instructions.md +3 -3
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill Loader Tests
|
|
3
|
+
*
|
|
4
|
+
* Tests for parsing SKILL.md files into typed SkillDefinition objects.
|
|
5
|
+
*/
|
|
6
|
+
import { describe, it } from 'node:test';
|
|
7
|
+
import assert from 'node:assert';
|
|
8
|
+
import { join } from 'node:path';
|
|
9
|
+
import { loadSkills, loadSkill, getSkillById, extractTriggers, buildTriggerMap, findMatchingSkills, DEFAULT_SKILLS_DIR, SKILL_FILE_NAME, } from './loader.js';
|
|
10
|
+
// Test against templates directory
|
|
11
|
+
const TEMPLATES_SKILLS_DIR = join(process.cwd(), 'templates', '.github', 'skills');
|
|
12
|
+
describe('Skill Loader', () => {
|
|
13
|
+
describe('loadSkills', () => {
|
|
14
|
+
it('should load all skills from templates directory', () => {
|
|
15
|
+
const result = loadSkills(TEMPLATES_SKILLS_DIR);
|
|
16
|
+
assert.strictEqual(result.errors.length, 0, `Unexpected errors: ${JSON.stringify(result.errors)}`);
|
|
17
|
+
assert.ok(result.skills.length >= 5, `Expected at least 5 skills, got ${result.skills.length}`);
|
|
18
|
+
// Check expected skills exist
|
|
19
|
+
const skillIds = result.skills.map((s) => s.id);
|
|
20
|
+
assert.ok(skillIds.includes('prd'), 'Should include prd skill');
|
|
21
|
+
assert.ok(skillIds.includes('shadcn-ui'), 'Should include shadcn-ui skill');
|
|
22
|
+
assert.ok(skillIds.includes('vercel-react-best-practices'), 'Should include react best practices skill');
|
|
23
|
+
});
|
|
24
|
+
it('should return error for non-existent directory', () => {
|
|
25
|
+
const result = loadSkills('/non/existent/path');
|
|
26
|
+
assert.strictEqual(result.skills.length, 0);
|
|
27
|
+
assert.strictEqual(result.errors.length, 1);
|
|
28
|
+
assert.ok(result.errors[0].message.includes('not found'));
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
describe('loadSkill', () => {
|
|
32
|
+
it('should parse PRD skill correctly', () => {
|
|
33
|
+
const filePath = join(TEMPLATES_SKILLS_DIR, 'prd', 'SKILL.md');
|
|
34
|
+
const result = loadSkill(filePath, 'prd');
|
|
35
|
+
assert.ok(!('error' in result), `Unexpected error: ${JSON.stringify(result)}`);
|
|
36
|
+
const { skill } = result;
|
|
37
|
+
assert.strictEqual(skill.id, 'prd');
|
|
38
|
+
assert.strictEqual(skill.frontmatter.name, 'prd');
|
|
39
|
+
assert.ok(skill.frontmatter.description?.includes('PRD'));
|
|
40
|
+
assert.ok(skill.body.length > 100, 'Should have substantial body content');
|
|
41
|
+
assert.ok(skill.triggers.length > 0, 'Should have extracted triggers');
|
|
42
|
+
});
|
|
43
|
+
it('should extract sourcePath correctly', () => {
|
|
44
|
+
const filePath = join(TEMPLATES_SKILLS_DIR, 'prd', 'SKILL.md');
|
|
45
|
+
const result = loadSkill(filePath, 'prd');
|
|
46
|
+
assert.ok(!('error' in result));
|
|
47
|
+
const { skill } = result;
|
|
48
|
+
assert.strictEqual(skill.sourcePath, filePath);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
describe('extractTriggers', () => {
|
|
52
|
+
it('should extract triggers from "Triggers on:" pattern', () => {
|
|
53
|
+
const description = 'Generate PRDs. Triggers on: create a prd, write prd for, plan this feature.';
|
|
54
|
+
const triggers = extractTriggers(description);
|
|
55
|
+
assert.ok(triggers.includes('create a prd'));
|
|
56
|
+
assert.ok(triggers.includes('write prd for'));
|
|
57
|
+
assert.ok(triggers.includes('plan this feature'));
|
|
58
|
+
});
|
|
59
|
+
it('should extract triggers from "Use when:" pattern', () => {
|
|
60
|
+
const description = 'Component library skill. Use when: shadcn, ui component, install button';
|
|
61
|
+
const triggers = extractTriggers(description);
|
|
62
|
+
assert.ok(triggers.includes('shadcn'));
|
|
63
|
+
assert.ok(triggers.includes('ui component'));
|
|
64
|
+
assert.ok(triggers.includes('install button'));
|
|
65
|
+
});
|
|
66
|
+
it('should extract quoted phrases', () => {
|
|
67
|
+
const description = 'Use when asked to "create a prd" or "write requirements"';
|
|
68
|
+
const triggers = extractTriggers(description);
|
|
69
|
+
assert.ok(triggers.includes('create a prd'));
|
|
70
|
+
assert.ok(triggers.includes('write requirements'));
|
|
71
|
+
});
|
|
72
|
+
it('should deduplicate triggers', () => {
|
|
73
|
+
const description = 'Triggers on: create a prd. Use "create a prd" often.';
|
|
74
|
+
const triggers = extractTriggers(description);
|
|
75
|
+
const prdCount = triggers.filter((t) => t === 'create a prd').length;
|
|
76
|
+
assert.strictEqual(prdCount, 1, 'Should not have duplicate triggers');
|
|
77
|
+
});
|
|
78
|
+
it('should return empty array for undefined description', () => {
|
|
79
|
+
const triggers = extractTriggers(undefined);
|
|
80
|
+
assert.deepStrictEqual(triggers, []);
|
|
81
|
+
});
|
|
82
|
+
it('should return empty array for description without triggers', () => {
|
|
83
|
+
const triggers = extractTriggers('Just a simple description with no trigger patterns.');
|
|
84
|
+
assert.deepStrictEqual(triggers, []);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
describe('buildTriggerMap', () => {
|
|
88
|
+
it('should build a map from triggers to skills', () => {
|
|
89
|
+
const result = loadSkills(TEMPLATES_SKILLS_DIR);
|
|
90
|
+
const triggerMap = buildTriggerMap(result);
|
|
91
|
+
assert.ok(triggerMap.size > 0, 'Should have triggers in map');
|
|
92
|
+
// PRD skill should be findable by its triggers
|
|
93
|
+
const prdSkill = result.skills.find((s) => s.id === 'prd');
|
|
94
|
+
if (prdSkill && prdSkill.triggers.length > 0) {
|
|
95
|
+
const mappedSkill = triggerMap.get(prdSkill.triggers[0]);
|
|
96
|
+
assert.ok(mappedSkill, 'Should find skill by trigger');
|
|
97
|
+
assert.strictEqual(mappedSkill?.id, 'prd');
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
it('should use lowercase keys for case-insensitive matching', () => {
|
|
101
|
+
const result = loadSkills(TEMPLATES_SKILLS_DIR);
|
|
102
|
+
const triggerMap = buildTriggerMap(result);
|
|
103
|
+
// All keys should be lowercase
|
|
104
|
+
for (const key of triggerMap.keys()) {
|
|
105
|
+
assert.strictEqual(key, key.toLowerCase(), `Key "${key}" should be lowercase`);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
describe('findMatchingSkills', () => {
|
|
110
|
+
it('should find skills matching a query', () => {
|
|
111
|
+
const result = loadSkills(TEMPLATES_SKILLS_DIR);
|
|
112
|
+
const triggerMap = buildTriggerMap(result);
|
|
113
|
+
// Find PRD skill with a relevant query
|
|
114
|
+
const prdSkill = result.skills.find((s) => s.id === 'prd');
|
|
115
|
+
if (prdSkill && prdSkill.triggers.length > 0) {
|
|
116
|
+
const query = `I want to ${prdSkill.triggers[0]} for my feature`;
|
|
117
|
+
const matches = findMatchingSkills(query, triggerMap);
|
|
118
|
+
assert.ok(matches.length > 0, 'Should find matching skill');
|
|
119
|
+
assert.ok(matches.some((s) => s.id === 'prd'), 'Should include PRD skill');
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
it('should be case-insensitive', () => {
|
|
123
|
+
const result = loadSkills(TEMPLATES_SKILLS_DIR);
|
|
124
|
+
const triggerMap = buildTriggerMap(result);
|
|
125
|
+
const matchesLower = findMatchingSkills('create a prd', triggerMap);
|
|
126
|
+
const matchesUpper = findMatchingSkills('CREATE A PRD', triggerMap);
|
|
127
|
+
assert.deepStrictEqual(matchesLower.map((s) => s.id), matchesUpper.map((s) => s.id), 'Case should not affect results');
|
|
128
|
+
});
|
|
129
|
+
it('should return empty array for non-matching query', () => {
|
|
130
|
+
const result = loadSkills(TEMPLATES_SKILLS_DIR);
|
|
131
|
+
const triggerMap = buildTriggerMap(result);
|
|
132
|
+
const matches = findMatchingSkills('xyzzy plugh nothing matches this', triggerMap);
|
|
133
|
+
assert.deepStrictEqual(matches, []);
|
|
134
|
+
});
|
|
135
|
+
it('should deduplicate skills that match multiple triggers', () => {
|
|
136
|
+
const result = loadSkills(TEMPLATES_SKILLS_DIR);
|
|
137
|
+
const triggerMap = buildTriggerMap(result);
|
|
138
|
+
// Find a skill with multiple triggers
|
|
139
|
+
const multiTriggerSkill = result.skills.find((s) => s.triggers.length > 1);
|
|
140
|
+
if (multiTriggerSkill) {
|
|
141
|
+
// Create a query that contains multiple triggers for the same skill
|
|
142
|
+
const query = multiTriggerSkill.triggers.join(' and ');
|
|
143
|
+
const matches = findMatchingSkills(query, triggerMap);
|
|
144
|
+
const skillCount = matches.filter((s) => s.id === multiTriggerSkill.id).length;
|
|
145
|
+
assert.strictEqual(skillCount, 1, 'Should not duplicate skills');
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
describe('getSkillById', () => {
|
|
150
|
+
it('should find skill by ID (case-insensitive)', () => {
|
|
151
|
+
const result = loadSkills(TEMPLATES_SKILLS_DIR);
|
|
152
|
+
const prd = getSkillById(result, 'PRD');
|
|
153
|
+
assert.ok(prd, 'Should find PRD skill');
|
|
154
|
+
assert.strictEqual(prd?.frontmatter.name, 'prd');
|
|
155
|
+
const prdLower = getSkillById(result, 'prd');
|
|
156
|
+
assert.strictEqual(prd?.id, prdLower?.id, 'Case should not matter');
|
|
157
|
+
});
|
|
158
|
+
it('should return undefined for non-existent skill', () => {
|
|
159
|
+
const result = loadSkills(TEMPLATES_SKILLS_DIR);
|
|
160
|
+
const notFound = getSkillById(result, 'non-existent-skill');
|
|
161
|
+
assert.strictEqual(notFound, undefined);
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
describe('constants', () => {
|
|
165
|
+
it('should export correct constants', () => {
|
|
166
|
+
assert.strictEqual(DEFAULT_SKILLS_DIR, '.github/skills');
|
|
167
|
+
assert.strictEqual(SKILL_FILE_NAME, 'SKILL.md');
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
describe('Skill frontmatter structure compatibility', () => {
|
|
172
|
+
it('all loaded skills should have valid frontmatter structure', () => {
|
|
173
|
+
const result = loadSkills(TEMPLATES_SKILLS_DIR);
|
|
174
|
+
for (const skill of result.skills) {
|
|
175
|
+
// Required fields
|
|
176
|
+
assert.ok(skill.frontmatter.name, `${skill.id} should have name`);
|
|
177
|
+
// Body should be non-empty
|
|
178
|
+
assert.ok(skill.body.length > 0, `${skill.id} should have body content`);
|
|
179
|
+
// Triggers should be an array
|
|
180
|
+
assert.ok(Array.isArray(skill.triggers), `${skill.id} triggers should be array`);
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
//# sourceMappingURL=loader.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.test.js","sourceRoot":"","sources":["../../../src/core/skills/loader.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill Schema Types
|
|
3
|
+
*
|
|
4
|
+
* Type definitions for Beth skills parsed from SKILL.md files.
|
|
5
|
+
* Skills are domain-specific knowledge modules that agents load on-demand.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Skill frontmatter parsed from YAML.
|
|
9
|
+
* This is the metadata block at the top of SKILL.md files.
|
|
10
|
+
*/
|
|
11
|
+
export interface SkillFrontmatter {
|
|
12
|
+
/** Display name of the skill */
|
|
13
|
+
name: string;
|
|
14
|
+
/** Description containing trigger phrases */
|
|
15
|
+
description?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Fully parsed skill definition.
|
|
19
|
+
* Combines frontmatter metadata with the markdown body (skill content).
|
|
20
|
+
*/
|
|
21
|
+
export interface SkillDefinition {
|
|
22
|
+
/** Unique identifier derived from directory name (e.g., 'prd' from 'skills/prd/SKILL.md') */
|
|
23
|
+
id: string;
|
|
24
|
+
/** Parsed YAML frontmatter */
|
|
25
|
+
frontmatter: SkillFrontmatter;
|
|
26
|
+
/** Raw markdown body (the skill content to inject) */
|
|
27
|
+
body: string;
|
|
28
|
+
/** Source file path for debugging */
|
|
29
|
+
sourcePath: string;
|
|
30
|
+
/** Trigger phrases extracted from description */
|
|
31
|
+
triggers: string[];
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Result of loading skills from a directory.
|
|
35
|
+
*/
|
|
36
|
+
export interface SkillLoadResult {
|
|
37
|
+
/** Successfully loaded skills */
|
|
38
|
+
skills: SkillDefinition[];
|
|
39
|
+
/** Errors encountered during loading */
|
|
40
|
+
errors: SkillLoadError[];
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Error encountered while loading a skill file.
|
|
44
|
+
*/
|
|
45
|
+
export interface SkillLoadError {
|
|
46
|
+
/** Path to the problematic file */
|
|
47
|
+
filePath: string;
|
|
48
|
+
/** Human-readable error message */
|
|
49
|
+
message: string;
|
|
50
|
+
/** Original error for debugging */
|
|
51
|
+
cause?: Error;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Map of trigger phrases to skill definitions.
|
|
55
|
+
* Used for quick lookup when processing user input.
|
|
56
|
+
*/
|
|
57
|
+
export type TriggerMap = Map<string, SkillDefinition>;
|
|
58
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/core/skills/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,gCAAgC;IAChC,IAAI,EAAE,MAAM,CAAC;IAEb,6CAA6C;IAC7C,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,6FAA6F;IAC7F,EAAE,EAAE,MAAM,CAAC;IAEX,8BAA8B;IAC9B,WAAW,EAAE,gBAAgB,CAAC;IAE9B,sDAAsD;IACtD,IAAI,EAAE,MAAM,CAAC;IAEb,qCAAqC;IACrC,UAAU,EAAE,MAAM,CAAC;IAEnB,iDAAiD;IACjD,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,iCAAiC;IACjC,MAAM,EAAE,eAAe,EAAE,CAAC;IAE1B,wCAAwC;IACxC,MAAM,EAAE,cAAc,EAAE,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,mCAAmC;IACnC,QAAQ,EAAE,MAAM,CAAC;IAEjB,mCAAmC;IACnC,OAAO,EAAE,MAAM,CAAC;IAEhB,mCAAmC;IACnC,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/core/skills/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Beth CLI - TypeScript Core
|
|
3
|
+
*
|
|
4
|
+
* Main exports for the Beth CLI runtime.
|
|
5
|
+
*/
|
|
6
|
+
export * from './core/agents/index.js';
|
|
7
|
+
export * from './core/skills/index.js';
|
|
8
|
+
export * from './lib/index.js';
|
|
9
|
+
export * from './cli/commands/index.js';
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,cAAc,wBAAwB,CAAC;AAGvC,cAAc,wBAAwB,CAAC;AAGvC,cAAc,gBAAgB,CAAC;AAG/B,cAAc,yBAAyB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Beth CLI - TypeScript Core
|
|
3
|
+
*
|
|
4
|
+
* Main exports for the Beth CLI runtime.
|
|
5
|
+
*/
|
|
6
|
+
// Core exports - Agents
|
|
7
|
+
export * from './core/agents/index.js';
|
|
8
|
+
// Core exports - Skills
|
|
9
|
+
export * from './core/skills/index.js';
|
|
10
|
+
// Library utilities
|
|
11
|
+
export * from './lib/index.js';
|
|
12
|
+
// CLI commands
|
|
13
|
+
export * from './cli/commands/index.js';
|
|
14
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,wBAAwB;AACxB,cAAc,wBAAwB,CAAC;AAEvC,wBAAwB;AACxB,cAAc,wBAAwB,CAAC;AAEvC,oBAAoB;AACpB,cAAc,gBAAgB,CAAC;AAE/B,eAAe;AACf,cAAc,yBAAyB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,qBAAqB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/lib/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Path validation utilities for user-supplied binary paths.
|
|
3
|
+
* Prevents path traversal attacks, injection, and execution of unintended binaries.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Validation result type
|
|
7
|
+
*/
|
|
8
|
+
export interface ValidationResult {
|
|
9
|
+
/** Whether the path is valid */
|
|
10
|
+
valid: boolean;
|
|
11
|
+
/** Error message if invalid */
|
|
12
|
+
error?: string;
|
|
13
|
+
/** Normalized absolute path if valid */
|
|
14
|
+
normalizedPath?: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Options for validateBinaryPath
|
|
18
|
+
*/
|
|
19
|
+
export interface ValidateBinaryPathOptions {
|
|
20
|
+
/** Require absolute path */
|
|
21
|
+
requireAbsolute?: boolean;
|
|
22
|
+
/** Check if file exists */
|
|
23
|
+
checkExists?: boolean;
|
|
24
|
+
/** Check if file is executable */
|
|
25
|
+
verifyExecutable?: boolean;
|
|
26
|
+
/** If provided, only allow these binary names */
|
|
27
|
+
allowedBasenames?: string[] | null;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Result of checking if a file is executable
|
|
31
|
+
*/
|
|
32
|
+
export interface ExecutableCheckResult {
|
|
33
|
+
exists: boolean;
|
|
34
|
+
executable: boolean;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Check if a path contains traversal sequences
|
|
38
|
+
* @param inputPath - The path to check
|
|
39
|
+
* @returns True if traversal sequences found
|
|
40
|
+
*/
|
|
41
|
+
export declare function containsTraversal(inputPath: string): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Check if a path contains shell injection characters
|
|
44
|
+
* @param inputPath - The path to check
|
|
45
|
+
* @returns True if injection characters found
|
|
46
|
+
*/
|
|
47
|
+
export declare function containsShellInjection(inputPath: string): boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Check if a file exists and is executable
|
|
50
|
+
* @param filePath - Absolute path to check
|
|
51
|
+
*/
|
|
52
|
+
export declare function checkExecutable(filePath: string): ExecutableCheckResult;
|
|
53
|
+
/**
|
|
54
|
+
* Validate a user-supplied binary path
|
|
55
|
+
* @param inputPath - The path provided by the user
|
|
56
|
+
* @param options - Validation options
|
|
57
|
+
*/
|
|
58
|
+
export declare function validateBinaryPath(inputPath: string, options?: ValidateBinaryPathOptions): ValidationResult;
|
|
59
|
+
/**
|
|
60
|
+
* Validate a binary path specifically for the beads (bd) CLI
|
|
61
|
+
* @param inputPath - The path to validate
|
|
62
|
+
*/
|
|
63
|
+
export declare function validateBeadsPath(inputPath: string): ValidationResult;
|
|
64
|
+
/**
|
|
65
|
+
* Validate a binary path specifically for the backlog CLI
|
|
66
|
+
* @param inputPath - The path to validate
|
|
67
|
+
*/
|
|
68
|
+
export declare function validateBacklogPath(inputPath: string): ValidationResult;
|
|
69
|
+
//# sourceMappingURL=pathValidation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pathValidation.d.ts","sourceRoot":"","sources":["../../src/lib/pathValidation.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAkBH;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,gCAAgC;IAChC,KAAK,EAAE,OAAO,CAAC;IACf,+BAA+B;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wCAAwC;IACxC,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC,4BAA4B;IAC5B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,2BAA2B;IAC3B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,kCAAkC;IAClC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,iDAAiD;IACjD,gBAAgB,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,EAAE,OAAO,CAAC;CACrB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAM5D;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAMjE;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,qBAAqB,CAkCvE;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,yBAA8B,GACtC,gBAAgB,CAqGlB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,gBAAgB,CAMrE;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,gBAAgB,CAMvE"}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Path validation utilities for user-supplied binary paths.
|
|
3
|
+
* Prevents path traversal attacks, injection, and execution of unintended binaries.
|
|
4
|
+
*/
|
|
5
|
+
import { existsSync, statSync, accessSync, constants } from 'fs';
|
|
6
|
+
import { resolve, normalize, isAbsolute } from 'path';
|
|
7
|
+
// Characters that could be used for shell injection
|
|
8
|
+
// Note: backslash is allowed as Windows path separator
|
|
9
|
+
const SHELL_INJECTION_CHARS = /[;&|`$(){}[\]<>!'"]/;
|
|
10
|
+
// Path traversal sequences
|
|
11
|
+
const TRAVERSAL_PATTERNS = [
|
|
12
|
+
/\.\.[/\\]/, // ../ or ..\
|
|
13
|
+
/[/\\]\.\.[/\\]/, // /../ or \..\
|
|
14
|
+
/[/\\]\.\.$/, // ends with /.. or \..
|
|
15
|
+
/^\.\.[/\\]/, // starts with ../ or ..\
|
|
16
|
+
/^\.\.$/, // just ".."
|
|
17
|
+
];
|
|
18
|
+
/**
|
|
19
|
+
* Check if a path contains traversal sequences
|
|
20
|
+
* @param inputPath - The path to check
|
|
21
|
+
* @returns True if traversal sequences found
|
|
22
|
+
*/
|
|
23
|
+
export function containsTraversal(inputPath) {
|
|
24
|
+
if (!inputPath || typeof inputPath !== 'string') {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
return TRAVERSAL_PATTERNS.some(pattern => pattern.test(inputPath));
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Check if a path contains shell injection characters
|
|
31
|
+
* @param inputPath - The path to check
|
|
32
|
+
* @returns True if injection characters found
|
|
33
|
+
*/
|
|
34
|
+
export function containsShellInjection(inputPath) {
|
|
35
|
+
if (!inputPath || typeof inputPath !== 'string') {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
return SHELL_INJECTION_CHARS.test(inputPath);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Check if a file exists and is executable
|
|
42
|
+
* @param filePath - Absolute path to check
|
|
43
|
+
*/
|
|
44
|
+
export function checkExecutable(filePath) {
|
|
45
|
+
const result = { exists: false, executable: false };
|
|
46
|
+
try {
|
|
47
|
+
if (!existsSync(filePath)) {
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
result.exists = true;
|
|
51
|
+
const stats = statSync(filePath);
|
|
52
|
+
if (!stats.isFile()) {
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
// On Windows, check file extension for executability
|
|
56
|
+
if (process.platform === 'win32') {
|
|
57
|
+
const executableExtensions = ['.exe', '.cmd', '.bat', '.com', '.ps1'];
|
|
58
|
+
const ext = filePath.toLowerCase().slice(filePath.lastIndexOf('.'));
|
|
59
|
+
result.executable = executableExtensions.includes(ext);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
// On Unix, check execute permission
|
|
63
|
+
try {
|
|
64
|
+
accessSync(filePath, constants.X_OK);
|
|
65
|
+
result.executable = true;
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
result.executable = false;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// File access error
|
|
74
|
+
}
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Validate a user-supplied binary path
|
|
79
|
+
* @param inputPath - The path provided by the user
|
|
80
|
+
* @param options - Validation options
|
|
81
|
+
*/
|
|
82
|
+
export function validateBinaryPath(inputPath, options = {}) {
|
|
83
|
+
const { requireAbsolute = false, checkExists = true, verifyExecutable = true, allowedBasenames = null, } = options;
|
|
84
|
+
// Basic type and empty check
|
|
85
|
+
if (!inputPath || typeof inputPath !== 'string') {
|
|
86
|
+
return { valid: false, error: 'Path cannot be empty' };
|
|
87
|
+
}
|
|
88
|
+
// Trim whitespace
|
|
89
|
+
const trimmedPath = inputPath.trim();
|
|
90
|
+
if (trimmedPath.length === 0) {
|
|
91
|
+
return { valid: false, error: 'Path cannot be empty' };
|
|
92
|
+
}
|
|
93
|
+
// Length limit to prevent DoS
|
|
94
|
+
if (trimmedPath.length > 4096) {
|
|
95
|
+
return { valid: false, error: 'Path exceeds maximum length (4096 characters)' };
|
|
96
|
+
}
|
|
97
|
+
// Check for null bytes (path injection)
|
|
98
|
+
if (trimmedPath.includes('\0')) {
|
|
99
|
+
return { valid: false, error: 'Path contains invalid characters (null byte)' };
|
|
100
|
+
}
|
|
101
|
+
// Check for path traversal
|
|
102
|
+
if (containsTraversal(trimmedPath)) {
|
|
103
|
+
return { valid: false, error: 'Path contains directory traversal sequences (../)' };
|
|
104
|
+
}
|
|
105
|
+
// Check for shell injection characters
|
|
106
|
+
if (containsShellInjection(trimmedPath)) {
|
|
107
|
+
return {
|
|
108
|
+
valid: false,
|
|
109
|
+
error: 'Path contains potentially dangerous characters. Use an absolute path without special characters.'
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
// Normalize the path
|
|
113
|
+
let normalizedPath;
|
|
114
|
+
try {
|
|
115
|
+
normalizedPath = normalize(trimmedPath);
|
|
116
|
+
// After normalization, check again for traversal (could be obfuscated)
|
|
117
|
+
if (containsTraversal(normalizedPath)) {
|
|
118
|
+
return { valid: false, error: 'Path resolves to a directory traversal' };
|
|
119
|
+
}
|
|
120
|
+
// Resolve to absolute path
|
|
121
|
+
normalizedPath = resolve(normalizedPath);
|
|
122
|
+
}
|
|
123
|
+
catch (err) {
|
|
124
|
+
const message = err instanceof Error ? err.message : 'unknown error';
|
|
125
|
+
return { valid: false, error: `Invalid path format: ${message}` };
|
|
126
|
+
}
|
|
127
|
+
// Check if absolute path is required
|
|
128
|
+
if (requireAbsolute && !isAbsolute(trimmedPath)) {
|
|
129
|
+
return { valid: false, error: 'Path must be an absolute path' };
|
|
130
|
+
}
|
|
131
|
+
// Check allowed basenames (whitelist specific binaries)
|
|
132
|
+
if (allowedBasenames && allowedBasenames.length > 0) {
|
|
133
|
+
// Extract basename handling both Unix and Windows separators
|
|
134
|
+
// This is necessary because on Unix, basename() doesn't handle Windows paths
|
|
135
|
+
const pathParts = normalizedPath.split(/[\\/]/);
|
|
136
|
+
const name = pathParts[pathParts.length - 1] || '';
|
|
137
|
+
const nameWithoutExt = name.replace(/\.(exe|cmd|bat|com)$/i, '');
|
|
138
|
+
const allowed = allowedBasenames.some(allowedName => {
|
|
139
|
+
const allowedLower = allowedName.toLowerCase();
|
|
140
|
+
return name.toLowerCase() === allowedLower ||
|
|
141
|
+
nameWithoutExt.toLowerCase() === allowedLower;
|
|
142
|
+
});
|
|
143
|
+
if (!allowed) {
|
|
144
|
+
return {
|
|
145
|
+
valid: false,
|
|
146
|
+
error: `Binary '${name}' is not in the allowed list: ${allowedBasenames.join(', ')}`
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
// Check if file exists
|
|
151
|
+
if (checkExists) {
|
|
152
|
+
const execCheck = checkExecutable(normalizedPath);
|
|
153
|
+
if (!execCheck.exists) {
|
|
154
|
+
return { valid: false, error: `File not found: ${normalizedPath}` };
|
|
155
|
+
}
|
|
156
|
+
// Check if executable
|
|
157
|
+
if (verifyExecutable && !execCheck.executable) {
|
|
158
|
+
return { valid: false, error: `File is not executable: ${normalizedPath}` };
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return { valid: true, normalizedPath };
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Validate a binary path specifically for the beads (bd) CLI
|
|
165
|
+
* @param inputPath - The path to validate
|
|
166
|
+
*/
|
|
167
|
+
export function validateBeadsPath(inputPath) {
|
|
168
|
+
return validateBinaryPath(inputPath, {
|
|
169
|
+
checkExists: true,
|
|
170
|
+
verifyExecutable: true,
|
|
171
|
+
allowedBasenames: ['bd', 'bd.exe', 'bd.cmd'],
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Validate a binary path specifically for the backlog CLI
|
|
176
|
+
* @param inputPath - The path to validate
|
|
177
|
+
*/
|
|
178
|
+
export function validateBacklogPath(inputPath) {
|
|
179
|
+
return validateBinaryPath(inputPath, {
|
|
180
|
+
checkExists: true,
|
|
181
|
+
verifyExecutable: true,
|
|
182
|
+
allowedBasenames: ['backlog', 'backlog.exe', 'backlog.cmd'],
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
//# sourceMappingURL=pathValidation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pathValidation.js","sourceRoot":"","sources":["../../src/lib/pathValidation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for TypeScript path validation utilities.
|
|
3
|
+
* Run with: node --test dist/lib/pathValidation.test.js
|
|
4
|
+
*
|
|
5
|
+
* These tests mirror the JavaScript tests in bin/lib/pathValidation.test.js
|
|
6
|
+
* to ensure the TypeScript port maintains the same behavior.
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=pathValidation.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pathValidation.test.d.ts","sourceRoot":"","sources":["../../src/lib/pathValidation.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
|