upfynai-code 2.8.2 → 2.8.4
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/agents/gitagent.js +67 -0
- package/dist/agents/index.js +2 -1
- package/dist/gitagent/index.js +9 -0
- package/dist/gitagent/parser.js +202 -0
- package/dist/gitagent/prompt-builder.js +153 -0
- package/package.json +2 -1
- package/scripts/prepublish.js +18 -1
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gitagent Agent
|
|
3
|
+
* Handles: gitagent-detect, gitagent-parse
|
|
4
|
+
*
|
|
5
|
+
* Relay-compatible actions for detecting and parsing gitagent directory structures
|
|
6
|
+
* on the user's local machine.
|
|
7
|
+
*/
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
import { promises as fsp } from 'fs';
|
|
10
|
+
import path from 'path';
|
|
11
|
+
import { detectGitagent, parseGitagentRepo } from '../gitagent/parser.js';
|
|
12
|
+
|
|
13
|
+
/** fs-backed helpers injected into the parser */
|
|
14
|
+
async function fsFileExists(filePath) {
|
|
15
|
+
try {
|
|
16
|
+
await fsp.access(filePath, fs.constants.F_OK);
|
|
17
|
+
return true;
|
|
18
|
+
} catch {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function fsReadFile(filePath) {
|
|
24
|
+
try {
|
|
25
|
+
return await fsp.readFile(filePath, 'utf8');
|
|
26
|
+
} catch {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function fsListDir(dirPath) {
|
|
32
|
+
try {
|
|
33
|
+
return await fsp.readdir(dirPath);
|
|
34
|
+
} catch {
|
|
35
|
+
return [];
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export default {
|
|
40
|
+
name: 'gitagent',
|
|
41
|
+
actions: {
|
|
42
|
+
/**
|
|
43
|
+
* Lightweight check for agent.yaml existence.
|
|
44
|
+
* @param {{ projectPath: string }} params
|
|
45
|
+
* @returns {{ detected: boolean }}
|
|
46
|
+
*/
|
|
47
|
+
'gitagent-detect': async (params) => {
|
|
48
|
+
const projectPath = params.projectPath;
|
|
49
|
+
if (!projectPath) return { detected: false };
|
|
50
|
+
const detected = await detectGitagent(projectPath, fsFileExists);
|
|
51
|
+
return { detected };
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Full parse — returns the complete GitagentDefinition as JSON.
|
|
56
|
+
* @param {{ projectPath: string }} params
|
|
57
|
+
* @returns {{ definition: object|null }}
|
|
58
|
+
*/
|
|
59
|
+
'gitagent-parse': async (params) => {
|
|
60
|
+
const projectPath = params.projectPath;
|
|
61
|
+
if (!projectPath) return { definition: null };
|
|
62
|
+
|
|
63
|
+
const definition = await parseGitagentRepo(projectPath, fsReadFile, fsListDir);
|
|
64
|
+
return { definition };
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
};
|
package/dist/agents/index.js
CHANGED
|
@@ -24,8 +24,9 @@ import filesAgent from './files.js';
|
|
|
24
24
|
import gitAgent from './git.js';
|
|
25
25
|
import execAgent from './exec.js';
|
|
26
26
|
import detectAgent from './detect.js';
|
|
27
|
+
import gitagentAgent from './gitagent.js';
|
|
27
28
|
|
|
28
|
-
const agents = [claudeAgent, codexAgent, cursorAgent, shellAgent, filesAgent, gitAgent, execAgent, detectAgent];
|
|
29
|
+
const agents = [claudeAgent, codexAgent, cursorAgent, shellAgent, filesAgent, gitAgent, execAgent, detectAgent, gitagentAgent];
|
|
29
30
|
|
|
30
31
|
/** Map of action name → handler function */
|
|
31
32
|
const actionMap = new Map();
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gitagent — Re-exports
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* import { detectGitagent, parseGitagentRepo } from 'upfynai-shared/gitagent';
|
|
6
|
+
* import { buildSystemPromptAppendix, mapGitagentModel } from 'upfynai-shared/gitagent';
|
|
7
|
+
*/
|
|
8
|
+
export { detectGitagent, parseGitagentRepo } from './parser.js';
|
|
9
|
+
export { buildSystemPromptAppendix, mapGitagentModel, extractAllowedTools } from './prompt-builder.js';
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gitagent Parser
|
|
3
|
+
*
|
|
4
|
+
* Reads a project directory following the gitagent spec (https://github.com/open-gitagent/gitagent)
|
|
5
|
+
* and produces a GitagentDefinition object. All file I/O is dependency-injected so the parser
|
|
6
|
+
* works for both local fs and relay file-read paths.
|
|
7
|
+
*
|
|
8
|
+
* @typedef {Object} GitagentDefinition
|
|
9
|
+
* @property {Object} manifest - Parsed agent.yaml
|
|
10
|
+
* @property {string|null} soul - SOUL.md content
|
|
11
|
+
* @property {string|null} rules - RULES.md content
|
|
12
|
+
* @property {string|null} agents - AGENTS.md content
|
|
13
|
+
* @property {Array} skills - Parsed skill definitions
|
|
14
|
+
* @property {Array} tools - Parsed tool schemas
|
|
15
|
+
* @property {Object|null} knowledge - Knowledge index + always-loaded docs
|
|
16
|
+
* @property {string|null} memory - MEMORY.md content
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import yaml from 'js-yaml';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Lightweight check: does agent.yaml exist in the project root?
|
|
23
|
+
* @param {string} projectPath - Absolute path to the project
|
|
24
|
+
* @param {(filePath: string) => Promise<boolean>} fileExists - Injected existence check
|
|
25
|
+
* @returns {Promise<boolean>}
|
|
26
|
+
*/
|
|
27
|
+
export async function detectGitagent(projectPath, fileExists) {
|
|
28
|
+
try {
|
|
29
|
+
return await fileExists(join(projectPath, 'agent.yaml'));
|
|
30
|
+
} catch {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Full parse of a gitagent directory into a definition object.
|
|
37
|
+
* @param {string} projectPath
|
|
38
|
+
* @param {(filePath: string) => Promise<string|null>} readFile - Returns file content or null
|
|
39
|
+
* @param {(dirPath: string) => Promise<string[]>} listDir - Returns entry names or []
|
|
40
|
+
* @returns {Promise<GitagentDefinition|null>}
|
|
41
|
+
*/
|
|
42
|
+
export async function parseGitagentRepo(projectPath, readFile, listDir) {
|
|
43
|
+
// Read manifest — required
|
|
44
|
+
const agentYamlContent = await readFile(join(projectPath, 'agent.yaml'));
|
|
45
|
+
if (!agentYamlContent) return null;
|
|
46
|
+
|
|
47
|
+
let manifest;
|
|
48
|
+
try {
|
|
49
|
+
manifest = yaml.load(agentYamlContent);
|
|
50
|
+
} catch {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
if (!manifest || typeof manifest !== 'object') return null;
|
|
54
|
+
|
|
55
|
+
// Read core markdown files (all optional)
|
|
56
|
+
const [soul, rules, agents, memory] = await Promise.all([
|
|
57
|
+
safeRead(readFile, join(projectPath, 'SOUL.md')),
|
|
58
|
+
safeRead(readFile, join(projectPath, 'RULES.md')),
|
|
59
|
+
safeRead(readFile, join(projectPath, 'AGENTS.md')),
|
|
60
|
+
safeRead(readFile, join(projectPath, 'memory', 'MEMORY.md')),
|
|
61
|
+
]);
|
|
62
|
+
|
|
63
|
+
// Parse skills
|
|
64
|
+
const skills = await parseSkills(projectPath, manifest.skills, readFile, listDir);
|
|
65
|
+
|
|
66
|
+
// Parse tools
|
|
67
|
+
const tools = await parseTools(projectPath, manifest.tools, readFile, listDir);
|
|
68
|
+
|
|
69
|
+
// Parse knowledge
|
|
70
|
+
const knowledge = await parseKnowledge(projectPath, readFile);
|
|
71
|
+
|
|
72
|
+
return { manifest, soul, rules, agents, skills, tools, knowledge, memory };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// ── Internal helpers ────────────────────────────────────────────────
|
|
76
|
+
|
|
77
|
+
function join(...parts) {
|
|
78
|
+
// Simple platform-agnostic join (works with / on all platforms in Node)
|
|
79
|
+
return parts.join('/').replace(/\/+/g, '/');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async function safeRead(readFile, filePath) {
|
|
83
|
+
try {
|
|
84
|
+
return await readFile(filePath) || null;
|
|
85
|
+
} catch {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Parse skills from the skills/ directory.
|
|
92
|
+
* Each skill is a subfolder containing SKILL.md with optional YAML frontmatter.
|
|
93
|
+
*/
|
|
94
|
+
async function parseSkills(projectPath, declaredSkills, readFile, listDir) {
|
|
95
|
+
const skillsDir = join(projectPath, 'skills');
|
|
96
|
+
let entries;
|
|
97
|
+
try {
|
|
98
|
+
entries = await listDir(skillsDir);
|
|
99
|
+
} catch {
|
|
100
|
+
return [];
|
|
101
|
+
}
|
|
102
|
+
if (!entries || entries.length === 0) return [];
|
|
103
|
+
|
|
104
|
+
const skills = [];
|
|
105
|
+
for (const entry of entries) {
|
|
106
|
+
const skillMd = await safeRead(readFile, join(skillsDir, entry, 'SKILL.md'));
|
|
107
|
+
if (!skillMd) continue;
|
|
108
|
+
|
|
109
|
+
const { frontmatter, body } = parseFrontmatter(skillMd);
|
|
110
|
+
skills.push({
|
|
111
|
+
name: entry,
|
|
112
|
+
...(frontmatter || {}),
|
|
113
|
+
description: body || null,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
return skills;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Parse tool definitions from tools/ directory.
|
|
121
|
+
* Each tool is a YAML file: tools/{name}.yaml
|
|
122
|
+
*/
|
|
123
|
+
async function parseTools(projectPath, declaredTools, readFile, listDir) {
|
|
124
|
+
const toolsDir = join(projectPath, 'tools');
|
|
125
|
+
let entries;
|
|
126
|
+
try {
|
|
127
|
+
entries = await listDir(toolsDir);
|
|
128
|
+
} catch {
|
|
129
|
+
return [];
|
|
130
|
+
}
|
|
131
|
+
if (!entries || entries.length === 0) return [];
|
|
132
|
+
|
|
133
|
+
const tools = [];
|
|
134
|
+
for (const entry of entries) {
|
|
135
|
+
if (!entry.endsWith('.yaml') && !entry.endsWith('.yml')) continue;
|
|
136
|
+
const content = await safeRead(readFile, join(toolsDir, entry));
|
|
137
|
+
if (!content) continue;
|
|
138
|
+
try {
|
|
139
|
+
const schema = yaml.load(content);
|
|
140
|
+
if (schema && typeof schema === 'object') {
|
|
141
|
+
tools.push({ name: entry.replace(/\.ya?ml$/, ''), ...schema });
|
|
142
|
+
}
|
|
143
|
+
} catch {
|
|
144
|
+
// skip invalid tool yaml
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return tools;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Parse knowledge index and load always_load documents.
|
|
152
|
+
*/
|
|
153
|
+
async function parseKnowledge(projectPath, readFile) {
|
|
154
|
+
const indexContent = await safeRead(readFile, join(projectPath, 'knowledge', 'index.yaml'));
|
|
155
|
+
if (!indexContent) return null;
|
|
156
|
+
|
|
157
|
+
let index;
|
|
158
|
+
try {
|
|
159
|
+
index = yaml.load(indexContent);
|
|
160
|
+
} catch {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
if (!index || typeof index !== 'object') return null;
|
|
164
|
+
|
|
165
|
+
// Load documents flagged as always_load
|
|
166
|
+
const alwaysLoadDocs = [];
|
|
167
|
+
const documents = index.documents || index.files || [];
|
|
168
|
+
for (const doc of documents) {
|
|
169
|
+
if (!doc.always_load) continue;
|
|
170
|
+
const docPath = doc.path || doc.file;
|
|
171
|
+
if (!docPath) continue;
|
|
172
|
+
const content = await safeRead(readFile, join(projectPath, 'knowledge', docPath));
|
|
173
|
+
if (content) {
|
|
174
|
+
alwaysLoadDocs.push({ path: docPath, title: doc.title || docPath, content });
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return { index, alwaysLoadDocs };
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Parse YAML frontmatter from a markdown file.
|
|
183
|
+
* Frontmatter is delimited by --- at the start of the file.
|
|
184
|
+
* @returns {{ frontmatter: object|null, body: string }}
|
|
185
|
+
*/
|
|
186
|
+
function parseFrontmatter(content) {
|
|
187
|
+
if (!content.startsWith('---')) {
|
|
188
|
+
return { frontmatter: null, body: content.trim() };
|
|
189
|
+
}
|
|
190
|
+
const endIdx = content.indexOf('---', 3);
|
|
191
|
+
if (endIdx === -1) {
|
|
192
|
+
return { frontmatter: null, body: content.trim() };
|
|
193
|
+
}
|
|
194
|
+
const fmRaw = content.slice(3, endIdx).trim();
|
|
195
|
+
const body = content.slice(endIdx + 3).trim();
|
|
196
|
+
try {
|
|
197
|
+
const frontmatter = yaml.load(fmRaw);
|
|
198
|
+
return { frontmatter: frontmatter && typeof frontmatter === 'object' ? frontmatter : null, body };
|
|
199
|
+
} catch {
|
|
200
|
+
return { frontmatter: null, body: content.trim() };
|
|
201
|
+
}
|
|
202
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gitagent Prompt Builder
|
|
3
|
+
*
|
|
4
|
+
* Converts a GitagentDefinition (from parser.js) into strings/configs
|
|
5
|
+
* that can be injected into Claude SDK options.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Model name mapping from gitagent spec model IDs to UpfynAI model IDs.
|
|
10
|
+
* Gitagent uses full Anthropic model IDs; we map to short names.
|
|
11
|
+
*/
|
|
12
|
+
const MODEL_MAP = {
|
|
13
|
+
'claude-opus-4-6': 'opus',
|
|
14
|
+
'claude-opus-4-20250514': 'opus',
|
|
15
|
+
'claude-sonnet-4-6': 'sonnet',
|
|
16
|
+
'claude-sonnet-4-5-20250929': 'sonnet',
|
|
17
|
+
'claude-haiku-4-5-20251001': 'haiku',
|
|
18
|
+
// Generic short names pass through
|
|
19
|
+
'opus': 'opus',
|
|
20
|
+
'sonnet': 'sonnet',
|
|
21
|
+
'haiku': 'haiku',
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Build a system prompt appendix from a parsed gitagent definition.
|
|
26
|
+
* Wrapped in <gitagent-context> tags so Claude can distinguish it.
|
|
27
|
+
*
|
|
28
|
+
* @param {import('./parser.js').GitagentDefinition} def
|
|
29
|
+
* @returns {string}
|
|
30
|
+
*/
|
|
31
|
+
export function buildSystemPromptAppendix(def) {
|
|
32
|
+
const sections = [];
|
|
33
|
+
|
|
34
|
+
// Identity header from manifest
|
|
35
|
+
const m = def.manifest;
|
|
36
|
+
sections.push(`# Gitagent: ${m.name} v${m.version || '0.0.0'}`);
|
|
37
|
+
if (m.description) sections.push(m.description);
|
|
38
|
+
|
|
39
|
+
// SOUL — agent identity & personality
|
|
40
|
+
if (def.soul) {
|
|
41
|
+
sections.push(`## Identity (SOUL)\n${def.soul}`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// RULES — hard constraints
|
|
45
|
+
if (def.rules) {
|
|
46
|
+
sections.push(`## Rules\n${def.rules}`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// AGENTS — framework-agnostic instructions
|
|
50
|
+
if (def.agents) {
|
|
51
|
+
sections.push(`## Agent Instructions\n${def.agents}`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Skills
|
|
55
|
+
if (def.skills && def.skills.length > 0) {
|
|
56
|
+
const skillLines = def.skills.map(s => {
|
|
57
|
+
const header = `### ${s.name}`;
|
|
58
|
+
const meta = [];
|
|
59
|
+
if (s.version) meta.push(`v${s.version}`);
|
|
60
|
+
if (s.tools && s.tools.length) meta.push(`tools: ${s.tools.join(', ')}`);
|
|
61
|
+
const metaLine = meta.length ? ` (${meta.join(' | ')})` : '';
|
|
62
|
+
return `${header}${metaLine}\n${s.description || ''}`;
|
|
63
|
+
});
|
|
64
|
+
sections.push(`## Skills\n${skillLines.join('\n\n')}`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Tools
|
|
68
|
+
if (def.tools && def.tools.length > 0) {
|
|
69
|
+
const toolLines = def.tools.map(t => {
|
|
70
|
+
const parts = [`### ${t.name}`];
|
|
71
|
+
if (t.description) parts.push(t.description);
|
|
72
|
+
if (t.input_schema || t.parameters) {
|
|
73
|
+
const schema = t.input_schema || t.parameters;
|
|
74
|
+
if (schema.properties) {
|
|
75
|
+
const props = Object.entries(schema.properties)
|
|
76
|
+
.map(([k, v]) => `- \`${k}\`: ${v.type || 'any'}${v.description ? ' — ' + v.description : ''}`)
|
|
77
|
+
.join('\n');
|
|
78
|
+
parts.push(`Parameters:\n${props}`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return parts.join('\n');
|
|
82
|
+
});
|
|
83
|
+
sections.push(`## Tools\n${toolLines.join('\n\n')}`);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Knowledge (always-loaded docs)
|
|
87
|
+
if (def.knowledge && def.knowledge.alwaysLoadDocs && def.knowledge.alwaysLoadDocs.length > 0) {
|
|
88
|
+
const knowledgeLines = def.knowledge.alwaysLoadDocs.map(d =>
|
|
89
|
+
`### ${d.title}\n${d.content}`
|
|
90
|
+
);
|
|
91
|
+
sections.push(`## Knowledge\n${knowledgeLines.join('\n\n')}`);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Memory
|
|
95
|
+
if (def.memory) {
|
|
96
|
+
sections.push(`## Memory\n${def.memory}`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Runtime constraints
|
|
100
|
+
if (m.runtime) {
|
|
101
|
+
const rc = [];
|
|
102
|
+
if (m.runtime.max_turns) rc.push(`Max turns: ${m.runtime.max_turns}`);
|
|
103
|
+
if (m.runtime.timeout) rc.push(`Timeout: ${m.runtime.timeout}s`);
|
|
104
|
+
if (rc.length) sections.push(`## Runtime\n${rc.join('\n')}`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return sections.join('\n\n');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Map a gitagent model config to an UpfynAI model ID.
|
|
112
|
+
* @param {Object|string} modelConfig - model field from agent.yaml
|
|
113
|
+
* @returns {string|null} UpfynAI model ID or null if no mapping
|
|
114
|
+
*/
|
|
115
|
+
export function mapGitagentModel(modelConfig) {
|
|
116
|
+
if (!modelConfig) return null;
|
|
117
|
+
|
|
118
|
+
// String shorthand: model: "claude-sonnet-4-5-20250929"
|
|
119
|
+
if (typeof modelConfig === 'string') {
|
|
120
|
+
return MODEL_MAP[modelConfig] || null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Object form: model: { preferred: "...", fallback: [...] }
|
|
124
|
+
if (modelConfig.preferred) {
|
|
125
|
+
return MODEL_MAP[modelConfig.preferred] || null;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Extract a deduplicated list of allowed tool names from all parsed skills.
|
|
133
|
+
* @param {Array} skills - Parsed skill definitions
|
|
134
|
+
* @returns {string[]}
|
|
135
|
+
*/
|
|
136
|
+
export function extractAllowedTools(skills) {
|
|
137
|
+
if (!skills || skills.length === 0) return [];
|
|
138
|
+
|
|
139
|
+
const toolSet = new Set();
|
|
140
|
+
for (const skill of skills) {
|
|
141
|
+
if (skill.tools && Array.isArray(skill.tools)) {
|
|
142
|
+
for (const t of skill.tools) {
|
|
143
|
+
toolSet.add(t);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (skill.allowed_tools && Array.isArray(skill.allowed_tools)) {
|
|
147
|
+
for (const t of skill.allowed_tools) {
|
|
148
|
+
toolSet.add(t);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return Array.from(toolSet);
|
|
153
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "upfynai-code",
|
|
3
|
-
"version": "2.8.
|
|
3
|
+
"version": "2.8.4",
|
|
4
4
|
"description": "Unified AI coding interface — access AI chat, terminal, file explorer, git, and visual canvas from any browser. Connect your local machine and code from anywhere.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -62,6 +62,7 @@
|
|
|
62
62
|
"commander": "^12.1.0",
|
|
63
63
|
"express": "^4.21.0",
|
|
64
64
|
"http-proxy-middleware": "^3.0.0",
|
|
65
|
+
"js-yaml": "^4.1.0",
|
|
65
66
|
"open": "^10.1.0",
|
|
66
67
|
"prompts": "^2.4.2",
|
|
67
68
|
"ws": "^8.18.0"
|
package/scripts/prepublish.js
CHANGED
|
@@ -24,6 +24,7 @@ const files = [
|
|
|
24
24
|
'index.js', 'utils.js',
|
|
25
25
|
'claude.js', 'codex.js', 'cursor.js',
|
|
26
26
|
'shell.js', 'files.js', 'git.js', 'exec.js', 'detect.js',
|
|
27
|
+
'gitagent.js',
|
|
27
28
|
];
|
|
28
29
|
|
|
29
30
|
let copied = 0;
|
|
@@ -38,4 +39,20 @@ for (const file of files) {
|
|
|
38
39
|
}
|
|
39
40
|
}
|
|
40
41
|
|
|
41
|
-
|
|
42
|
+
// Copy gitagent module (parser + prompt-builder) — needed by gitagent agent
|
|
43
|
+
const sharedGitagent = join(cliRoot, '..', 'shared', 'gitagent');
|
|
44
|
+
const distGitagent = join(cliRoot, 'dist', 'gitagent');
|
|
45
|
+
if (existsSync(distGitagent)) rmSync(distGitagent, { recursive: true });
|
|
46
|
+
mkdirSync(distGitagent, { recursive: true });
|
|
47
|
+
|
|
48
|
+
const gitagentFiles = ['index.js', 'parser.js', 'prompt-builder.js'];
|
|
49
|
+
for (const file of gitagentFiles) {
|
|
50
|
+
const src = join(sharedGitagent, file);
|
|
51
|
+
const dest = join(distGitagent, file);
|
|
52
|
+
if (existsSync(src)) {
|
|
53
|
+
cpSync(src, dest);
|
|
54
|
+
copied++;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
console.log(` [prepublish] Copied ${copied} files to dist/`);
|