codemaxxing 0.1.13 → 0.2.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/dist/agent.d.ts +6 -0
- package/dist/agent.js +19 -1
- package/dist/index.js +271 -3
- package/dist/skills/registry.d.ts +13 -0
- package/dist/skills/registry.js +472 -0
- package/dist/themes.js +37 -77
- package/dist/utils/context.d.ts +1 -1
- package/dist/utils/context.js +6 -2
- package/dist/utils/skills.d.ts +60 -0
- package/dist/utils/skills.js +203 -0
- package/package.json +1 -1
- package/src/agent.ts +24 -1
- package/src/index.tsx +316 -1
- package/src/skills/registry.ts +482 -0
- package/src/themes.ts +39 -77
- package/src/utils/context.ts +8 -2
- package/src/utils/skills.ts +241 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { type RegistrySkill } from "../skills/registry.js";
|
|
2
|
+
export interface SkillMeta {
|
|
3
|
+
name: string;
|
|
4
|
+
description: string;
|
|
5
|
+
version: string;
|
|
6
|
+
author: string;
|
|
7
|
+
tags: string[];
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* List all installed skills by scanning ~/.codemaxxing/skills/
|
|
11
|
+
*/
|
|
12
|
+
export declare function listInstalledSkills(): SkillMeta[];
|
|
13
|
+
/**
|
|
14
|
+
* Install a skill from the built-in registry
|
|
15
|
+
*/
|
|
16
|
+
export declare function installSkill(name: string): {
|
|
17
|
+
ok: boolean;
|
|
18
|
+
message: string;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Remove an installed skill
|
|
22
|
+
*/
|
|
23
|
+
export declare function removeSkill(name: string): {
|
|
24
|
+
ok: boolean;
|
|
25
|
+
message: string;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Get the prompt.md content for an installed skill
|
|
29
|
+
*/
|
|
30
|
+
export declare function getSkillPrompt(name: string): string | null;
|
|
31
|
+
/**
|
|
32
|
+
* Get skills that should be active for the given project directory.
|
|
33
|
+
* If .codemaxxing/skills.json exists in the project, only those skills are active.
|
|
34
|
+
* Otherwise, all installed skills are active.
|
|
35
|
+
*/
|
|
36
|
+
export declare function getActiveSkills(cwd: string, sessionDisabled?: Set<string>): string[];
|
|
37
|
+
/**
|
|
38
|
+
* Build the skill prompt blocks to inject into the system prompt
|
|
39
|
+
*/
|
|
40
|
+
export declare function buildSkillPrompts(cwd: string, sessionDisabled?: Set<string>): string;
|
|
41
|
+
/**
|
|
42
|
+
* Create a scaffold for a new custom skill
|
|
43
|
+
*/
|
|
44
|
+
export declare function createSkillScaffold(name: string): {
|
|
45
|
+
ok: boolean;
|
|
46
|
+
message: string;
|
|
47
|
+
path?: string;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Search the built-in registry by name, tags, or description
|
|
51
|
+
*/
|
|
52
|
+
export declare function searchRegistry(query: string): RegistrySkill[];
|
|
53
|
+
/**
|
|
54
|
+
* Return all skills from the built-in registry
|
|
55
|
+
*/
|
|
56
|
+
export declare function getRegistrySkills(): RegistrySkill[];
|
|
57
|
+
/**
|
|
58
|
+
* Get the count of active skills
|
|
59
|
+
*/
|
|
60
|
+
export declare function getActiveSkillCount(cwd: string, sessionDisabled?: Set<string>): number;
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync, rmSync } from "fs";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import { homedir } from "os";
|
|
4
|
+
import { REGISTRY } from "../skills/registry.js";
|
|
5
|
+
const SKILLS_DIR = join(homedir(), ".codemaxxing", "skills");
|
|
6
|
+
/**
|
|
7
|
+
* Ensure the skills directory exists
|
|
8
|
+
*/
|
|
9
|
+
function ensureSkillsDir() {
|
|
10
|
+
if (!existsSync(SKILLS_DIR)) {
|
|
11
|
+
mkdirSync(SKILLS_DIR, { recursive: true });
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* List all installed skills by scanning ~/.codemaxxing/skills/
|
|
16
|
+
*/
|
|
17
|
+
export function listInstalledSkills() {
|
|
18
|
+
ensureSkillsDir();
|
|
19
|
+
const skills = [];
|
|
20
|
+
try {
|
|
21
|
+
const entries = readdirSync(SKILLS_DIR, { withFileTypes: true });
|
|
22
|
+
for (const entry of entries) {
|
|
23
|
+
if (!entry.isDirectory())
|
|
24
|
+
continue;
|
|
25
|
+
const metaPath = join(SKILLS_DIR, entry.name, "skill.json");
|
|
26
|
+
if (!existsSync(metaPath))
|
|
27
|
+
continue;
|
|
28
|
+
try {
|
|
29
|
+
const meta = JSON.parse(readFileSync(metaPath, "utf-8"));
|
|
30
|
+
skills.push({
|
|
31
|
+
name: meta.name ?? entry.name,
|
|
32
|
+
description: meta.description ?? "",
|
|
33
|
+
version: meta.version ?? "0.0.0",
|
|
34
|
+
author: meta.author ?? "unknown",
|
|
35
|
+
tags: meta.tags ?? [],
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// skip malformed skill.json
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
// directory doesn't exist or can't be read
|
|
45
|
+
}
|
|
46
|
+
return skills;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Install a skill from the built-in registry
|
|
50
|
+
*/
|
|
51
|
+
export function installSkill(name) {
|
|
52
|
+
const skill = REGISTRY.find((s) => s.name === name);
|
|
53
|
+
if (!skill) {
|
|
54
|
+
return { ok: false, message: `Skill "${name}" not found in registry` };
|
|
55
|
+
}
|
|
56
|
+
ensureSkillsDir();
|
|
57
|
+
const skillDir = join(SKILLS_DIR, name);
|
|
58
|
+
if (existsSync(skillDir)) {
|
|
59
|
+
return { ok: false, message: `Skill "${name}" is already installed` };
|
|
60
|
+
}
|
|
61
|
+
mkdirSync(skillDir, { recursive: true });
|
|
62
|
+
mkdirSync(join(skillDir, "examples"), { recursive: true });
|
|
63
|
+
// Write skill.json
|
|
64
|
+
const meta = {
|
|
65
|
+
name: skill.name,
|
|
66
|
+
description: skill.description,
|
|
67
|
+
version: skill.version,
|
|
68
|
+
author: skill.author,
|
|
69
|
+
tags: skill.tags,
|
|
70
|
+
};
|
|
71
|
+
writeFileSync(join(skillDir, "skill.json"), JSON.stringify(meta, null, 2));
|
|
72
|
+
// Write prompt.md
|
|
73
|
+
writeFileSync(join(skillDir, "prompt.md"), skill.prompt);
|
|
74
|
+
return { ok: true, message: `Installed skill: ${skill.name}` };
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Remove an installed skill
|
|
78
|
+
*/
|
|
79
|
+
export function removeSkill(name) {
|
|
80
|
+
const skillDir = join(SKILLS_DIR, name);
|
|
81
|
+
if (!existsSync(skillDir)) {
|
|
82
|
+
return { ok: false, message: `Skill "${name}" is not installed` };
|
|
83
|
+
}
|
|
84
|
+
rmSync(skillDir, { recursive: true, force: true });
|
|
85
|
+
return { ok: true, message: `Removed skill: ${name}` };
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Get the prompt.md content for an installed skill
|
|
89
|
+
*/
|
|
90
|
+
export function getSkillPrompt(name) {
|
|
91
|
+
const promptPath = join(SKILLS_DIR, name, "prompt.md");
|
|
92
|
+
if (!existsSync(promptPath))
|
|
93
|
+
return null;
|
|
94
|
+
return readFileSync(promptPath, "utf-8");
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Get examples from a skill's examples/ directory
|
|
98
|
+
*/
|
|
99
|
+
function getSkillExamples(name) {
|
|
100
|
+
const examplesDir = join(SKILLS_DIR, name, "examples");
|
|
101
|
+
if (!existsSync(examplesDir))
|
|
102
|
+
return [];
|
|
103
|
+
try {
|
|
104
|
+
return readdirSync(examplesDir)
|
|
105
|
+
.filter((f) => f.endsWith(".md"))
|
|
106
|
+
.map((f) => readFileSync(join(examplesDir, f), "utf-8"));
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
return [];
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Get skills that should be active for the given project directory.
|
|
114
|
+
* If .codemaxxing/skills.json exists in the project, only those skills are active.
|
|
115
|
+
* Otherwise, all installed skills are active.
|
|
116
|
+
*/
|
|
117
|
+
export function getActiveSkills(cwd, sessionDisabled = new Set()) {
|
|
118
|
+
const installed = listInstalledSkills().map((s) => s.name);
|
|
119
|
+
let active;
|
|
120
|
+
const projectConfig = join(cwd, ".codemaxxing", "skills.json");
|
|
121
|
+
if (existsSync(projectConfig)) {
|
|
122
|
+
try {
|
|
123
|
+
const config = JSON.parse(readFileSync(projectConfig, "utf-8"));
|
|
124
|
+
const projectSkills = config.skills ?? [];
|
|
125
|
+
// Only include project skills that are actually installed
|
|
126
|
+
active = projectSkills.filter((s) => installed.includes(s));
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
active = installed;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
active = installed;
|
|
134
|
+
}
|
|
135
|
+
// Filter out session-disabled skills
|
|
136
|
+
return active.filter((s) => !sessionDisabled.has(s));
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Build the skill prompt blocks to inject into the system prompt
|
|
140
|
+
*/
|
|
141
|
+
export function buildSkillPrompts(cwd, sessionDisabled = new Set()) {
|
|
142
|
+
const activeSkills = getActiveSkills(cwd, sessionDisabled);
|
|
143
|
+
if (activeSkills.length === 0)
|
|
144
|
+
return "";
|
|
145
|
+
const blocks = [];
|
|
146
|
+
for (const name of activeSkills) {
|
|
147
|
+
const prompt = getSkillPrompt(name);
|
|
148
|
+
if (!prompt)
|
|
149
|
+
continue;
|
|
150
|
+
blocks.push(`\n--- Skill: ${name} ---`);
|
|
151
|
+
blocks.push(prompt.trim());
|
|
152
|
+
// Include examples if any
|
|
153
|
+
const examples = getSkillExamples(name);
|
|
154
|
+
for (const example of examples) {
|
|
155
|
+
blocks.push(`\n### Example:\n${example.trim()}`);
|
|
156
|
+
}
|
|
157
|
+
blocks.push(`--- End Skill ---`);
|
|
158
|
+
}
|
|
159
|
+
return blocks.join("\n");
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Create a scaffold for a new custom skill
|
|
163
|
+
*/
|
|
164
|
+
export function createSkillScaffold(name) {
|
|
165
|
+
ensureSkillsDir();
|
|
166
|
+
const skillDir = join(SKILLS_DIR, name);
|
|
167
|
+
if (existsSync(skillDir)) {
|
|
168
|
+
return { ok: false, message: `Skill "${name}" already exists` };
|
|
169
|
+
}
|
|
170
|
+
mkdirSync(skillDir, { recursive: true });
|
|
171
|
+
mkdirSync(join(skillDir, "examples"), { recursive: true });
|
|
172
|
+
const meta = {
|
|
173
|
+
name,
|
|
174
|
+
description: "A custom skill",
|
|
175
|
+
version: "1.0.0",
|
|
176
|
+
author: "you",
|
|
177
|
+
tags: [],
|
|
178
|
+
};
|
|
179
|
+
writeFileSync(join(skillDir, "skill.json"), JSON.stringify(meta, null, 2));
|
|
180
|
+
writeFileSync(join(skillDir, "prompt.md"), `# ${name}\n\nAdd your skill prompt here. This content will be injected into the system prompt.\n\n## Guidelines\n- Be specific and actionable\n- Include best practices\n- List anti-patterns to avoid\n`);
|
|
181
|
+
return { ok: true, message: `Created skill scaffold: ${name}`, path: skillDir };
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Search the built-in registry by name, tags, or description
|
|
185
|
+
*/
|
|
186
|
+
export function searchRegistry(query) {
|
|
187
|
+
const q = query.toLowerCase();
|
|
188
|
+
return REGISTRY.filter((s) => s.name.toLowerCase().includes(q) ||
|
|
189
|
+
s.description.toLowerCase().includes(q) ||
|
|
190
|
+
s.tags.some((t) => t.toLowerCase().includes(q)));
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Return all skills from the built-in registry
|
|
194
|
+
*/
|
|
195
|
+
export function getRegistrySkills() {
|
|
196
|
+
return REGISTRY;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Get the count of active skills
|
|
200
|
+
*/
|
|
201
|
+
export function getActiveSkillCount(cwd, sessionDisabled = new Set()) {
|
|
202
|
+
return getActiveSkills(cwd, sessionDisabled).length;
|
|
203
|
+
}
|
package/package.json
CHANGED
package/src/agent.ts
CHANGED
|
@@ -8,6 +8,7 @@ import type {
|
|
|
8
8
|
import { FILE_TOOLS, executeTool, generateDiff, getExistingContent } from "./tools/files.js";
|
|
9
9
|
import { buildProjectContext, getSystemPrompt } from "./utils/context.js";
|
|
10
10
|
import { isGitRepo, autoCommit } from "./utils/git.js";
|
|
11
|
+
import { buildSkillPrompts, getActiveSkillCount } from "./utils/skills.js";
|
|
11
12
|
import { createSession, saveMessage, updateTokenEstimate, updateSessionCost, loadMessages } from "./utils/sessions.js";
|
|
12
13
|
import type { ProviderConfig } from "./config.js";
|
|
13
14
|
|
|
@@ -99,6 +100,7 @@ export class CodingAgent {
|
|
|
99
100
|
private totalCost: number = 0;
|
|
100
101
|
private systemPrompt: string = "";
|
|
101
102
|
private compressionThreshold: number;
|
|
103
|
+
private sessionDisabledSkills: Set<string> = new Set();
|
|
102
104
|
|
|
103
105
|
constructor(private options: AgentOptions) {
|
|
104
106
|
this.providerType = options.provider.type || "openai";
|
|
@@ -128,7 +130,8 @@ export class CodingAgent {
|
|
|
128
130
|
*/
|
|
129
131
|
async init(): Promise<void> {
|
|
130
132
|
const context = await buildProjectContext(this.cwd);
|
|
131
|
-
|
|
133
|
+
const skillPrompts = buildSkillPrompts(this.cwd, this.sessionDisabledSkills);
|
|
134
|
+
this.systemPrompt = await getSystemPrompt(context, skillPrompts);
|
|
132
135
|
|
|
133
136
|
this.messages = [
|
|
134
137
|
{ role: "system", content: this.systemPrompt },
|
|
@@ -689,6 +692,26 @@ export class CodingAgent {
|
|
|
689
692
|
};
|
|
690
693
|
}
|
|
691
694
|
|
|
695
|
+
disableSkill(name: string): void {
|
|
696
|
+
this.sessionDisabledSkills.add(name);
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
enableSkill(name: string): void {
|
|
700
|
+
this.sessionDisabledSkills.delete(name);
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
getSessionDisabledSkills(): Set<string> {
|
|
704
|
+
return this.sessionDisabledSkills;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
getActiveSkillCount(): number {
|
|
708
|
+
return getActiveSkillCount(this.cwd, this.sessionDisabledSkills);
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
getCwd(): string {
|
|
712
|
+
return this.cwd;
|
|
713
|
+
}
|
|
714
|
+
|
|
692
715
|
reset(): void {
|
|
693
716
|
const systemMsg = this.messages[0];
|
|
694
717
|
this.messages = [systemMsg];
|