@nodesify/astria 1.0.0 → 1.0.1
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/LICENSE +21 -21
- package/README.md +65 -65
- package/dist/commands/add.d.ts +7 -7
- package/dist/commands/add.js +33 -33
- package/dist/commands/affected.d.ts +5 -5
- package/dist/commands/affected.js +29 -29
- package/dist/commands/cluster.d.ts +1 -1
- package/dist/commands/cluster.js +50 -50
- package/dist/commands/diagnose.d.ts +4 -4
- package/dist/commands/diagnose.js +19 -19
- package/dist/commands/diff.d.ts +1 -1
- package/dist/commands/diff.js +35 -35
- package/dist/commands/explain.d.ts +3 -3
- package/dist/commands/explain.js +37 -37
- package/dist/commands/export.d.ts +6 -6
- package/dist/commands/export.js +32 -32
- package/dist/commands/feedback.d.ts +11 -11
- package/dist/commands/feedback.js +40 -40
- package/dist/commands/global.d.ts +6 -6
- package/dist/commands/global.js +60 -60
- package/dist/commands/history.d.ts +4 -4
- package/dist/commands/history.js +27 -27
- package/dist/commands/hook-guard.d.ts +1 -1
- package/dist/commands/hook-guard.js +144 -144
- package/dist/commands/hook.d.ts +2 -2
- package/dist/commands/hook.js +52 -52
- package/dist/commands/install.d.ts +2 -2
- package/dist/commands/install.js +39 -39
- package/dist/commands/map.d.ts +5 -5
- package/dist/commands/map.js +15 -15
- package/dist/commands/mcp.d.ts +3 -3
- package/dist/commands/mcp.js +14 -14
- package/dist/commands/merge.d.ts +1 -1
- package/dist/commands/merge.js +50 -50
- package/dist/commands/migrate.d.ts +3 -3
- package/dist/commands/migrate.js +74 -74
- package/dist/commands/path.d.ts +5 -5
- package/dist/commands/path.js +18 -18
- package/dist/commands/prs.d.ts +4 -4
- package/dist/commands/prs.js +132 -132
- package/dist/commands/query.d.ts +9 -9
- package/dist/commands/query.js +21 -21
- package/dist/commands/run.d.ts +9 -9
- package/dist/commands/run.js +68 -68
- package/dist/commands/stats.d.ts +3 -3
- package/dist/commands/stats.js +17 -17
- package/dist/commands/status.d.ts +3 -3
- package/dist/commands/status.js +56 -56
- package/dist/commands/tree.d.ts +5 -5
- package/dist/commands/tree.js +52 -52
- package/dist/commands/update.d.ts +6 -6
- package/dist/commands/update.js +65 -65
- package/dist/commands/watch.d.ts +3 -3
- package/dist/commands/watch.js +113 -113
- package/dist/commands/wiki.d.ts +6 -6
- package/dist/commands/wiki.js +59 -59
- package/dist/index.d.ts +4 -4
- package/dist/index.js +249 -249
- package/dist/install/hooks.d.ts +11 -11
- package/dist/install/hooks.js +306 -306
- package/dist/install/index.d.ts +2 -2
- package/dist/install/index.js +361 -361
- package/dist/install/markdown-inject.d.ts +6 -6
- package/dist/install/markdown-inject.js +177 -177
- package/dist/install/platforms.d.ts +12 -12
- package/dist/install/platforms.js +129 -129
- package/dist/install/settings-inject.d.ts +17 -17
- package/dist/install/settings-inject.js +424 -424
- package/dist/native.d.ts +31 -31
- package/dist/native.js +106 -106
- package/package.json +72 -72
- package/skills/skill-aider.md +40 -40
- package/skills/skill-codex.md +46 -46
- package/skills/skill-copilot.md +46 -46
- package/skills/skill-cursor.md +39 -39
- package/skills/skill-gemini.md +46 -46
- package/skills/skill-kiro.md +39 -39
- package/skills/skill-opencode.md +44 -44
- package/skills/skill-trae.md +46 -46
- package/skills/skill.md +266 -266
- package/dist/graphify.node +0 -4
package/dist/install/index.js
CHANGED
|
@@ -1,362 +1,362 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.installPlatform = installPlatform;
|
|
37
|
-
exports.uninstallPlatform = uninstallPlatform;
|
|
38
|
-
const fs = __importStar(require("fs"));
|
|
39
|
-
const path = __importStar(require("path"));
|
|
40
|
-
const os = __importStar(require("os"));
|
|
41
|
-
const platforms_1 = require("./platforms");
|
|
42
|
-
const markdown_inject_1 = require("./markdown-inject");
|
|
43
|
-
const settings_inject_1 = require("./settings-inject");
|
|
44
|
-
function sectionMessage(result, added, updated, unchanged) {
|
|
45
|
-
return result === 'added' ? added : result === 'updated' ? updated : unchanged;
|
|
46
|
-
}
|
|
47
|
-
const MCP_LABELS = {
|
|
48
|
-
zcode: { name: 'ZCode MCP server', file: '.zcode/config.json' },
|
|
49
|
-
claude: { name: 'Claude MCP server', file: '.mcp.json' },
|
|
50
|
-
cursor: { name: 'Cursor MCP server', file: '.cursor/mcp.json' },
|
|
51
|
-
gemini: { name: 'Gemini MCP server', file: '.gemini/settings.json' },
|
|
52
|
-
};
|
|
53
|
-
function getSkillDir() {
|
|
54
|
-
return path.resolve(__dirname, '..', '..', 'skills');
|
|
55
|
-
}
|
|
56
|
-
/// CLAUDE_CONFIG_DIR is read exactly once, at load, and sanitized to a
|
|
57
|
-
/// normalized absolute path without traversal segments (or undefined). No
|
|
58
|
-
/// other code path reads it, so env input can never reach a filesystem
|
|
59
|
-
/// write unvalidated.
|
|
60
|
-
const CLAUDE_CONFIG_DIR = (() => {
|
|
61
|
-
const raw = process.env.CLAUDE_CONFIG_DIR;
|
|
62
|
-
if (!raw)
|
|
63
|
-
return undefined;
|
|
64
|
-
const resolved = path.resolve(raw);
|
|
65
|
-
return raw === resolved && !raw.includes('..') && path.isAbsolute(raw)
|
|
66
|
-
? resolved
|
|
67
|
-
: undefined;
|
|
68
|
-
})();
|
|
69
|
-
function copySkillFile(platform, cfg) {
|
|
70
|
-
const messages = [];
|
|
71
|
-
if (!cfg.skillFile)
|
|
72
|
-
return messages;
|
|
73
|
-
// skillDst comes from the static platform table; refuse traversal
|
|
74
|
-
// segments anyway so no runtime value can redirect the install target.
|
|
75
|
-
if (cfg.skillDst.split(/[\\/]/).includes('..')) {
|
|
76
|
-
messages.push(`Refusing unsafe skill destination: ${cfg.skillDst}`);
|
|
77
|
-
return messages;
|
|
78
|
-
}
|
|
79
|
-
const src = path.join(getSkillDir(), cfg.skillFile);
|
|
80
|
-
if (!fs.existsSync(src)) {
|
|
81
|
-
messages.push(`Skill file not found: ${src}`);
|
|
82
|
-
return messages;
|
|
83
|
-
}
|
|
84
|
-
const homeDir = os.homedir();
|
|
85
|
-
const dst = path.join(homeDir, cfg.skillDst);
|
|
86
|
-
if (platform === 'claude' && CLAUDE_CONFIG_DIR) {
|
|
87
|
-
const overrideDst = path.join(CLAUDE_CONFIG_DIR, 'skills', 'astria', 'SKILL.md');
|
|
88
|
-
copyFile(src, overrideDst);
|
|
89
|
-
messages.push(`Skill file -> ${overrideDst}`);
|
|
90
|
-
return messages;
|
|
91
|
-
}
|
|
92
|
-
copyFile(src, dst);
|
|
93
|
-
messages.push(`Skill file -> ${dst}`);
|
|
94
|
-
return messages;
|
|
95
|
-
}
|
|
96
|
-
function copyFile(src, dst) {
|
|
97
|
-
// Sink guard: only normalized absolute destinations without traversal
|
|
98
|
-
// segments may be written, regardless of how the caller derived them.
|
|
99
|
-
if (!path.isAbsolute(dst) || dst.split(/[\\/]/).includes('..')) {
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
102
|
-
const dir = path.dirname(dst);
|
|
103
|
-
if (!fs.existsSync(dir)) {
|
|
104
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
105
|
-
}
|
|
106
|
-
fs.copyFileSync(src, dst);
|
|
107
|
-
}
|
|
108
|
-
function writeInstallStamp(dir) {
|
|
109
|
-
const stampPath = path.join(dir, '.astria_version');
|
|
110
|
-
// Same guard as copyFile, applied to the exact value that is written:
|
|
111
|
-
// only a normalized absolute path without traversal segments may reach
|
|
112
|
-
// the filesystem, regardless of how the caller derived it.
|
|
113
|
-
if (!path.isAbsolute(stampPath) || stampPath.split(/[\\/]/).includes('..')) {
|
|
114
|
-
return;
|
|
115
|
-
}
|
|
116
|
-
try {
|
|
117
|
-
fs.writeFileSync(stampPath, require('../../package.json').version + '\n', 'utf-8');
|
|
118
|
-
}
|
|
119
|
-
catch { /* ignore */ }
|
|
120
|
-
}
|
|
121
|
-
/// Pre-1.0 installs wrote skills under `skills/graphify/`. The path carries
|
|
122
|
-
/// exactly one `astria` segment (the skill dir name), so the legacy
|
|
123
|
-
/// destination is derivable by swapping that segment.
|
|
124
|
-
function legacySkillDst(cfg) {
|
|
125
|
-
return cfg.skillDst.replace(/([\\/])astria([\\/])/, '$1graphify$2');
|
|
126
|
-
}
|
|
127
|
-
/// Removes the pre-1.0 skill file (and its now-empty folder) so a stale
|
|
128
|
-
/// graphify skill is never left behind after install or uninstall.
|
|
129
|
-
function removeLegacySkillFile(platform, cfg) {
|
|
130
|
-
const messages = [];
|
|
131
|
-
if (!cfg.skillFile)
|
|
132
|
-
return messages;
|
|
133
|
-
const targets = [path.join(os.homedir(), legacySkillDst(cfg))];
|
|
134
|
-
if (platform === 'claude' && CLAUDE_CONFIG_DIR) {
|
|
135
|
-
targets.push(path.join(CLAUDE_CONFIG_DIR, 'skills', 'graphify', 'SKILL.md'));
|
|
136
|
-
}
|
|
137
|
-
for (const legacy of targets) {
|
|
138
|
-
const dir = path.dirname(legacy);
|
|
139
|
-
const hadDir = fs.existsSync(dir);
|
|
140
|
-
if (fs.existsSync(legacy)) {
|
|
141
|
-
try {
|
|
142
|
-
fs.unlinkSync(legacy);
|
|
143
|
-
messages.push(`Legacy skill file removed: ${legacy}`);
|
|
144
|
-
}
|
|
145
|
-
catch { /* unreadable — leave it */ }
|
|
146
|
-
}
|
|
147
|
-
if (!hadDir)
|
|
148
|
-
continue;
|
|
149
|
-
// Install stamps from both eras; a folder holding only these is removed
|
|
150
|
-
// outright so no stale skill directory lingers.
|
|
151
|
-
for (const stamp of ['.astria_version', '.graphify_version']) {
|
|
152
|
-
try {
|
|
153
|
-
fs.unlinkSync(path.join(dir, stamp));
|
|
154
|
-
}
|
|
155
|
-
catch { /* absent */ }
|
|
156
|
-
}
|
|
157
|
-
try {
|
|
158
|
-
fs.rmdirSync(dir);
|
|
159
|
-
}
|
|
160
|
-
catch { /* not empty — leave */ }
|
|
161
|
-
}
|
|
162
|
-
return messages;
|
|
163
|
-
}
|
|
164
|
-
function installPlatform(platform, projectDir) {
|
|
165
|
-
const messages = [];
|
|
166
|
-
if (platform === 'cursor') {
|
|
167
|
-
if ((0, settings_inject_1.injectCursorRule)(projectDir)) {
|
|
168
|
-
messages.push('Cursor rule -> .cursor/rules/astria.mdc');
|
|
169
|
-
}
|
|
170
|
-
else {
|
|
171
|
-
messages.push('Cursor rule: already installed');
|
|
172
|
-
}
|
|
173
|
-
messages.push((0, settings_inject_1.injectAgentMcp)(projectDir, 'cursor')
|
|
174
|
-
? 'Cursor MCP server -> .cursor/mcp.json'
|
|
175
|
-
: 'Cursor MCP server: already registered');
|
|
176
|
-
return messages;
|
|
177
|
-
}
|
|
178
|
-
if (platform === 'kiro') {
|
|
179
|
-
const cfg = platforms_1.PLATFORMS.kiro;
|
|
180
|
-
messages.push(...copySkillFile('kiro', cfg));
|
|
181
|
-
messages.push(...removeLegacySkillFile('kiro', cfg));
|
|
182
|
-
if ((0, settings_inject_1.injectKiroSteering)(projectDir)) {
|
|
183
|
-
messages.push('Kiro steering -> .kiro/steering/astria.md');
|
|
184
|
-
}
|
|
185
|
-
else {
|
|
186
|
-
messages.push('Kiro steering: already installed');
|
|
187
|
-
}
|
|
188
|
-
return messages;
|
|
189
|
-
}
|
|
190
|
-
const cfg = platforms_1.PLATFORMS[platform];
|
|
191
|
-
if (!cfg) {
|
|
192
|
-
messages.push(`Unknown platform: ${platform}. Available: ${Object.keys(platforms_1.PLATFORMS).join(', ')}`);
|
|
193
|
-
return messages;
|
|
194
|
-
}
|
|
195
|
-
messages.push(...copySkillFile(platform, cfg));
|
|
196
|
-
messages.push(...removeLegacySkillFile(platform, cfg));
|
|
197
|
-
if (cfg.claudeMd) {
|
|
198
|
-
const claudeMdPath = path.join(os.homedir(), '.claude', 'CLAUDE.md');
|
|
199
|
-
const registration = (0, markdown_inject_1.injectSection)(claudeMdPath, markdown_inject_1.SKILL_REGISTRATION);
|
|
200
|
-
messages.push(sectionMessage(registration, 'User CLAUDE.md: skill registration added', 'User CLAUDE.md: skill registration updated', 'User CLAUDE.md: already registered'));
|
|
201
|
-
}
|
|
202
|
-
const projectMd = path.join(projectDir, 'CLAUDE.md');
|
|
203
|
-
if (cfg.claudeMd || cfg.agentsMd || cfg.geminiMd) {
|
|
204
|
-
if (cfg.claudeMd) {
|
|
205
|
-
const result = (0, markdown_inject_1.injectSection)(projectMd, markdown_inject_1.PROJECT_MD_SECTION);
|
|
206
|
-
messages.push(sectionMessage(result, 'Project CLAUDE.md: astria section added', 'Project CLAUDE.md: astria section updated', 'Project CLAUDE.md: astria section unchanged'));
|
|
207
|
-
}
|
|
208
|
-
if (cfg.agentsMd) {
|
|
209
|
-
const agentsMd = path.join(projectDir, 'AGENTS.md');
|
|
210
|
-
const result = (0, markdown_inject_1.injectSection)(agentsMd, markdown_inject_1.PROJECT_MD_SECTION);
|
|
211
|
-
messages.push(sectionMessage(result, 'Project AGENTS.md: astria section added', 'Project AGENTS.md: astria section updated', 'Project AGENTS.md: astria section unchanged'));
|
|
212
|
-
}
|
|
213
|
-
if (cfg.geminiMd) {
|
|
214
|
-
const geminiMd = path.join(projectDir, 'GEMINI.md');
|
|
215
|
-
const result = (0, markdown_inject_1.injectSection)(geminiMd, markdown_inject_1.PROJECT_MD_SECTION);
|
|
216
|
-
messages.push(sectionMessage(result, 'Project GEMINI.md: astria section added', 'Project GEMINI.md: astria section updated', 'Project GEMINI.md: astria section unchanged'));
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
switch (cfg.settingsHook) {
|
|
220
|
-
case 'claude':
|
|
221
|
-
if ((0, settings_inject_1.injectClaudeHook)(projectDir)) {
|
|
222
|
-
messages.push('Claude PreToolUse hook -> .claude/settings.json');
|
|
223
|
-
}
|
|
224
|
-
else {
|
|
225
|
-
messages.push('Claude PreToolUse hook: already installed');
|
|
226
|
-
}
|
|
227
|
-
break;
|
|
228
|
-
case 'codex':
|
|
229
|
-
if ((0, settings_inject_1.injectCodexHook)(projectDir)) {
|
|
230
|
-
messages.push('Codex PreToolUse hook -> .codex/hooks.json');
|
|
231
|
-
}
|
|
232
|
-
else {
|
|
233
|
-
messages.push('Codex PreToolUse hook: already installed');
|
|
234
|
-
}
|
|
235
|
-
break;
|
|
236
|
-
case 'gemini':
|
|
237
|
-
if ((0, settings_inject_1.injectGeminiHook)(projectDir)) {
|
|
238
|
-
messages.push('Gemini BeforeTool hook -> .gemini/settings.json');
|
|
239
|
-
}
|
|
240
|
-
else {
|
|
241
|
-
messages.push('Gemini BeforeTool hook: already installed');
|
|
242
|
-
}
|
|
243
|
-
break;
|
|
244
|
-
case 'opencode':
|
|
245
|
-
if ((0, settings_inject_1.injectOpenCodePlugin)(projectDir)) {
|
|
246
|
-
messages.push('OpenCode plugin -> .opencode/plugins/astria.js');
|
|
247
|
-
}
|
|
248
|
-
else {
|
|
249
|
-
messages.push('OpenCode plugin: already installed');
|
|
250
|
-
}
|
|
251
|
-
break;
|
|
252
|
-
}
|
|
253
|
-
if (cfg.mcp) {
|
|
254
|
-
const label = MCP_LABELS[cfg.mcp];
|
|
255
|
-
messages.push((0, settings_inject_1.injectAgentMcp)(projectDir, cfg.mcp)
|
|
256
|
-
? `${label.name} -> ${label.file}`
|
|
257
|
-
: `${label.name}: already registered`);
|
|
258
|
-
}
|
|
259
|
-
return messages;
|
|
260
|
-
}
|
|
261
|
-
function uninstallPlatform(platform, projectDir) {
|
|
262
|
-
const messages = [];
|
|
263
|
-
if (platform === 'cursor') {
|
|
264
|
-
if ((0, settings_inject_1.removeCursorRule)(projectDir)) {
|
|
265
|
-
messages.push('Cursor rule: removed');
|
|
266
|
-
}
|
|
267
|
-
else {
|
|
268
|
-
messages.push('Cursor rule: not found');
|
|
269
|
-
}
|
|
270
|
-
messages.push((0, settings_inject_1.removeAgentMcp)(projectDir, 'cursor')
|
|
271
|
-
? 'Cursor MCP server: removed'
|
|
272
|
-
: 'Cursor MCP server: not found');
|
|
273
|
-
return messages;
|
|
274
|
-
}
|
|
275
|
-
if (platform === 'kiro') {
|
|
276
|
-
const cfg = platforms_1.PLATFORMS.kiro;
|
|
277
|
-
if (cfg.skillFile) {
|
|
278
|
-
const homeDir = os.homedir();
|
|
279
|
-
const dst = path.join(homeDir, cfg.skillDst);
|
|
280
|
-
try {
|
|
281
|
-
fs.unlinkSync(dst);
|
|
282
|
-
messages.push(`Skill file removed: ${dst}`);
|
|
283
|
-
}
|
|
284
|
-
catch {
|
|
285
|
-
messages.push('Skill file: not found');
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
messages.push(...removeLegacySkillFile('kiro', cfg));
|
|
289
|
-
if ((0, settings_inject_1.removeKiroSteering)(projectDir)) {
|
|
290
|
-
messages.push('Kiro steering: removed');
|
|
291
|
-
}
|
|
292
|
-
else {
|
|
293
|
-
messages.push('Kiro steering: not found');
|
|
294
|
-
}
|
|
295
|
-
return messages;
|
|
296
|
-
}
|
|
297
|
-
const cfg = platforms_1.PLATFORMS[platform];
|
|
298
|
-
if (!cfg) {
|
|
299
|
-
messages.push(`Unknown platform: ${platform}`);
|
|
300
|
-
return messages;
|
|
301
|
-
}
|
|
302
|
-
if (cfg.skillFile) {
|
|
303
|
-
const homeDir = os.homedir();
|
|
304
|
-
let dst = path.join(homeDir, cfg.skillDst);
|
|
305
|
-
if (platform === 'claude') {
|
|
306
|
-
const configDir = process.env.CLAUDE_CONFIG_DIR;
|
|
307
|
-
if (configDir)
|
|
308
|
-
dst = path.join(configDir, 'skills', 'astria', 'SKILL.md');
|
|
309
|
-
}
|
|
310
|
-
try {
|
|
311
|
-
fs.unlinkSync(dst);
|
|
312
|
-
messages.push(`Skill file removed: ${dst}`);
|
|
313
|
-
}
|
|
314
|
-
catch {
|
|
315
|
-
messages.push('Skill file: not found');
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
messages.push(...removeLegacySkillFile(platform, cfg));
|
|
319
|
-
if (cfg.claudeMd) {
|
|
320
|
-
const claudeMdPath = path.join(os.homedir(), '.claude', 'CLAUDE.md');
|
|
321
|
-
(0, markdown_inject_1.removeSection)(claudeMdPath);
|
|
322
|
-
messages.push('User CLAUDE.md: astria section removed');
|
|
323
|
-
}
|
|
324
|
-
if (cfg.claudeMd) {
|
|
325
|
-
(0, markdown_inject_1.removeSection)(path.join(projectDir, 'CLAUDE.md'));
|
|
326
|
-
messages.push('Project CLAUDE.md: astria section removed');
|
|
327
|
-
}
|
|
328
|
-
if (cfg.agentsMd) {
|
|
329
|
-
(0, markdown_inject_1.removeSection)(path.join(projectDir, 'AGENTS.md'));
|
|
330
|
-
messages.push('Project AGENTS.md: astria section removed');
|
|
331
|
-
}
|
|
332
|
-
if (cfg.geminiMd) {
|
|
333
|
-
(0, markdown_inject_1.removeSection)(path.join(projectDir, 'GEMINI.md'));
|
|
334
|
-
messages.push('Project GEMINI.md: astria section removed');
|
|
335
|
-
}
|
|
336
|
-
switch (cfg.settingsHook) {
|
|
337
|
-
case 'claude':
|
|
338
|
-
(0, settings_inject_1.removeClaudeHook)(projectDir);
|
|
339
|
-
messages.push('Claude PreToolUse hook: removed');
|
|
340
|
-
break;
|
|
341
|
-
case 'codex':
|
|
342
|
-
(0, settings_inject_1.removeCodexHook)(projectDir);
|
|
343
|
-
messages.push('Codex PreToolUse hook: removed');
|
|
344
|
-
break;
|
|
345
|
-
case 'gemini':
|
|
346
|
-
(0, settings_inject_1.removeGeminiHook)(projectDir);
|
|
347
|
-
messages.push('Gemini BeforeTool hook: removed');
|
|
348
|
-
break;
|
|
349
|
-
case 'opencode':
|
|
350
|
-
(0, settings_inject_1.removeOpenCodePlugin)(projectDir);
|
|
351
|
-
messages.push('OpenCode plugin: removed');
|
|
352
|
-
break;
|
|
353
|
-
}
|
|
354
|
-
if (cfg.mcp) {
|
|
355
|
-
const label = MCP_LABELS[cfg.mcp];
|
|
356
|
-
messages.push((0, settings_inject_1.removeAgentMcp)(projectDir, cfg.mcp)
|
|
357
|
-
? `${label.name}: removed`
|
|
358
|
-
: `${label.name}: not found`);
|
|
359
|
-
}
|
|
360
|
-
return messages;
|
|
361
|
-
}
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.installPlatform = installPlatform;
|
|
37
|
+
exports.uninstallPlatform = uninstallPlatform;
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
const path = __importStar(require("path"));
|
|
40
|
+
const os = __importStar(require("os"));
|
|
41
|
+
const platforms_1 = require("./platforms");
|
|
42
|
+
const markdown_inject_1 = require("./markdown-inject");
|
|
43
|
+
const settings_inject_1 = require("./settings-inject");
|
|
44
|
+
function sectionMessage(result, added, updated, unchanged) {
|
|
45
|
+
return result === 'added' ? added : result === 'updated' ? updated : unchanged;
|
|
46
|
+
}
|
|
47
|
+
const MCP_LABELS = {
|
|
48
|
+
zcode: { name: 'ZCode MCP server', file: '.zcode/config.json' },
|
|
49
|
+
claude: { name: 'Claude MCP server', file: '.mcp.json' },
|
|
50
|
+
cursor: { name: 'Cursor MCP server', file: '.cursor/mcp.json' },
|
|
51
|
+
gemini: { name: 'Gemini MCP server', file: '.gemini/settings.json' },
|
|
52
|
+
};
|
|
53
|
+
function getSkillDir() {
|
|
54
|
+
return path.resolve(__dirname, '..', '..', 'skills');
|
|
55
|
+
}
|
|
56
|
+
/// CLAUDE_CONFIG_DIR is read exactly once, at load, and sanitized to a
|
|
57
|
+
/// normalized absolute path without traversal segments (or undefined). No
|
|
58
|
+
/// other code path reads it, so env input can never reach a filesystem
|
|
59
|
+
/// write unvalidated.
|
|
60
|
+
const CLAUDE_CONFIG_DIR = (() => {
|
|
61
|
+
const raw = process.env.CLAUDE_CONFIG_DIR;
|
|
62
|
+
if (!raw)
|
|
63
|
+
return undefined;
|
|
64
|
+
const resolved = path.resolve(raw);
|
|
65
|
+
return raw === resolved && !raw.includes('..') && path.isAbsolute(raw)
|
|
66
|
+
? resolved
|
|
67
|
+
: undefined;
|
|
68
|
+
})();
|
|
69
|
+
function copySkillFile(platform, cfg) {
|
|
70
|
+
const messages = [];
|
|
71
|
+
if (!cfg.skillFile)
|
|
72
|
+
return messages;
|
|
73
|
+
// skillDst comes from the static platform table; refuse traversal
|
|
74
|
+
// segments anyway so no runtime value can redirect the install target.
|
|
75
|
+
if (cfg.skillDst.split(/[\\/]/).includes('..')) {
|
|
76
|
+
messages.push(`Refusing unsafe skill destination: ${cfg.skillDst}`);
|
|
77
|
+
return messages;
|
|
78
|
+
}
|
|
79
|
+
const src = path.join(getSkillDir(), cfg.skillFile);
|
|
80
|
+
if (!fs.existsSync(src)) {
|
|
81
|
+
messages.push(`Skill file not found: ${src}`);
|
|
82
|
+
return messages;
|
|
83
|
+
}
|
|
84
|
+
const homeDir = os.homedir();
|
|
85
|
+
const dst = path.join(homeDir, cfg.skillDst);
|
|
86
|
+
if (platform === 'claude' && CLAUDE_CONFIG_DIR) {
|
|
87
|
+
const overrideDst = path.join(CLAUDE_CONFIG_DIR, 'skills', 'astria', 'SKILL.md');
|
|
88
|
+
copyFile(src, overrideDst);
|
|
89
|
+
messages.push(`Skill file -> ${overrideDst}`);
|
|
90
|
+
return messages;
|
|
91
|
+
}
|
|
92
|
+
copyFile(src, dst);
|
|
93
|
+
messages.push(`Skill file -> ${dst}`);
|
|
94
|
+
return messages;
|
|
95
|
+
}
|
|
96
|
+
function copyFile(src, dst) {
|
|
97
|
+
// Sink guard: only normalized absolute destinations without traversal
|
|
98
|
+
// segments may be written, regardless of how the caller derived them.
|
|
99
|
+
if (!path.isAbsolute(dst) || dst.split(/[\\/]/).includes('..')) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const dir = path.dirname(dst);
|
|
103
|
+
if (!fs.existsSync(dir)) {
|
|
104
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
105
|
+
}
|
|
106
|
+
fs.copyFileSync(src, dst);
|
|
107
|
+
}
|
|
108
|
+
function writeInstallStamp(dir) {
|
|
109
|
+
const stampPath = path.join(dir, '.astria_version');
|
|
110
|
+
// Same guard as copyFile, applied to the exact value that is written:
|
|
111
|
+
// only a normalized absolute path without traversal segments may reach
|
|
112
|
+
// the filesystem, regardless of how the caller derived it.
|
|
113
|
+
if (!path.isAbsolute(stampPath) || stampPath.split(/[\\/]/).includes('..')) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
try {
|
|
117
|
+
fs.writeFileSync(stampPath, require('../../package.json').version + '\n', 'utf-8');
|
|
118
|
+
}
|
|
119
|
+
catch { /* ignore */ }
|
|
120
|
+
}
|
|
121
|
+
/// Pre-1.0 installs wrote skills under `skills/graphify/`. The path carries
|
|
122
|
+
/// exactly one `astria` segment (the skill dir name), so the legacy
|
|
123
|
+
/// destination is derivable by swapping that segment.
|
|
124
|
+
function legacySkillDst(cfg) {
|
|
125
|
+
return cfg.skillDst.replace(/([\\/])astria([\\/])/, '$1graphify$2');
|
|
126
|
+
}
|
|
127
|
+
/// Removes the pre-1.0 skill file (and its now-empty folder) so a stale
|
|
128
|
+
/// graphify skill is never left behind after install or uninstall.
|
|
129
|
+
function removeLegacySkillFile(platform, cfg) {
|
|
130
|
+
const messages = [];
|
|
131
|
+
if (!cfg.skillFile)
|
|
132
|
+
return messages;
|
|
133
|
+
const targets = [path.join(os.homedir(), legacySkillDst(cfg))];
|
|
134
|
+
if (platform === 'claude' && CLAUDE_CONFIG_DIR) {
|
|
135
|
+
targets.push(path.join(CLAUDE_CONFIG_DIR, 'skills', 'graphify', 'SKILL.md'));
|
|
136
|
+
}
|
|
137
|
+
for (const legacy of targets) {
|
|
138
|
+
const dir = path.dirname(legacy);
|
|
139
|
+
const hadDir = fs.existsSync(dir);
|
|
140
|
+
if (fs.existsSync(legacy)) {
|
|
141
|
+
try {
|
|
142
|
+
fs.unlinkSync(legacy);
|
|
143
|
+
messages.push(`Legacy skill file removed: ${legacy}`);
|
|
144
|
+
}
|
|
145
|
+
catch { /* unreadable — leave it */ }
|
|
146
|
+
}
|
|
147
|
+
if (!hadDir)
|
|
148
|
+
continue;
|
|
149
|
+
// Install stamps from both eras; a folder holding only these is removed
|
|
150
|
+
// outright so no stale skill directory lingers.
|
|
151
|
+
for (const stamp of ['.astria_version', '.graphify_version']) {
|
|
152
|
+
try {
|
|
153
|
+
fs.unlinkSync(path.join(dir, stamp));
|
|
154
|
+
}
|
|
155
|
+
catch { /* absent */ }
|
|
156
|
+
}
|
|
157
|
+
try {
|
|
158
|
+
fs.rmdirSync(dir);
|
|
159
|
+
}
|
|
160
|
+
catch { /* not empty — leave */ }
|
|
161
|
+
}
|
|
162
|
+
return messages;
|
|
163
|
+
}
|
|
164
|
+
function installPlatform(platform, projectDir) {
|
|
165
|
+
const messages = [];
|
|
166
|
+
if (platform === 'cursor') {
|
|
167
|
+
if ((0, settings_inject_1.injectCursorRule)(projectDir)) {
|
|
168
|
+
messages.push('Cursor rule -> .cursor/rules/astria.mdc');
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
messages.push('Cursor rule: already installed');
|
|
172
|
+
}
|
|
173
|
+
messages.push((0, settings_inject_1.injectAgentMcp)(projectDir, 'cursor')
|
|
174
|
+
? 'Cursor MCP server -> .cursor/mcp.json'
|
|
175
|
+
: 'Cursor MCP server: already registered');
|
|
176
|
+
return messages;
|
|
177
|
+
}
|
|
178
|
+
if (platform === 'kiro') {
|
|
179
|
+
const cfg = platforms_1.PLATFORMS.kiro;
|
|
180
|
+
messages.push(...copySkillFile('kiro', cfg));
|
|
181
|
+
messages.push(...removeLegacySkillFile('kiro', cfg));
|
|
182
|
+
if ((0, settings_inject_1.injectKiroSteering)(projectDir)) {
|
|
183
|
+
messages.push('Kiro steering -> .kiro/steering/astria.md');
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
messages.push('Kiro steering: already installed');
|
|
187
|
+
}
|
|
188
|
+
return messages;
|
|
189
|
+
}
|
|
190
|
+
const cfg = platforms_1.PLATFORMS[platform];
|
|
191
|
+
if (!cfg) {
|
|
192
|
+
messages.push(`Unknown platform: ${platform}. Available: ${Object.keys(platforms_1.PLATFORMS).join(', ')}`);
|
|
193
|
+
return messages;
|
|
194
|
+
}
|
|
195
|
+
messages.push(...copySkillFile(platform, cfg));
|
|
196
|
+
messages.push(...removeLegacySkillFile(platform, cfg));
|
|
197
|
+
if (cfg.claudeMd) {
|
|
198
|
+
const claudeMdPath = path.join(os.homedir(), '.claude', 'CLAUDE.md');
|
|
199
|
+
const registration = (0, markdown_inject_1.injectSection)(claudeMdPath, markdown_inject_1.SKILL_REGISTRATION);
|
|
200
|
+
messages.push(sectionMessage(registration, 'User CLAUDE.md: skill registration added', 'User CLAUDE.md: skill registration updated', 'User CLAUDE.md: already registered'));
|
|
201
|
+
}
|
|
202
|
+
const projectMd = path.join(projectDir, 'CLAUDE.md');
|
|
203
|
+
if (cfg.claudeMd || cfg.agentsMd || cfg.geminiMd) {
|
|
204
|
+
if (cfg.claudeMd) {
|
|
205
|
+
const result = (0, markdown_inject_1.injectSection)(projectMd, markdown_inject_1.PROJECT_MD_SECTION);
|
|
206
|
+
messages.push(sectionMessage(result, 'Project CLAUDE.md: astria section added', 'Project CLAUDE.md: astria section updated', 'Project CLAUDE.md: astria section unchanged'));
|
|
207
|
+
}
|
|
208
|
+
if (cfg.agentsMd) {
|
|
209
|
+
const agentsMd = path.join(projectDir, 'AGENTS.md');
|
|
210
|
+
const result = (0, markdown_inject_1.injectSection)(agentsMd, markdown_inject_1.PROJECT_MD_SECTION);
|
|
211
|
+
messages.push(sectionMessage(result, 'Project AGENTS.md: astria section added', 'Project AGENTS.md: astria section updated', 'Project AGENTS.md: astria section unchanged'));
|
|
212
|
+
}
|
|
213
|
+
if (cfg.geminiMd) {
|
|
214
|
+
const geminiMd = path.join(projectDir, 'GEMINI.md');
|
|
215
|
+
const result = (0, markdown_inject_1.injectSection)(geminiMd, markdown_inject_1.PROJECT_MD_SECTION);
|
|
216
|
+
messages.push(sectionMessage(result, 'Project GEMINI.md: astria section added', 'Project GEMINI.md: astria section updated', 'Project GEMINI.md: astria section unchanged'));
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
switch (cfg.settingsHook) {
|
|
220
|
+
case 'claude':
|
|
221
|
+
if ((0, settings_inject_1.injectClaudeHook)(projectDir)) {
|
|
222
|
+
messages.push('Claude PreToolUse hook -> .claude/settings.json');
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
messages.push('Claude PreToolUse hook: already installed');
|
|
226
|
+
}
|
|
227
|
+
break;
|
|
228
|
+
case 'codex':
|
|
229
|
+
if ((0, settings_inject_1.injectCodexHook)(projectDir)) {
|
|
230
|
+
messages.push('Codex PreToolUse hook -> .codex/hooks.json');
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
messages.push('Codex PreToolUse hook: already installed');
|
|
234
|
+
}
|
|
235
|
+
break;
|
|
236
|
+
case 'gemini':
|
|
237
|
+
if ((0, settings_inject_1.injectGeminiHook)(projectDir)) {
|
|
238
|
+
messages.push('Gemini BeforeTool hook -> .gemini/settings.json');
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
messages.push('Gemini BeforeTool hook: already installed');
|
|
242
|
+
}
|
|
243
|
+
break;
|
|
244
|
+
case 'opencode':
|
|
245
|
+
if ((0, settings_inject_1.injectOpenCodePlugin)(projectDir)) {
|
|
246
|
+
messages.push('OpenCode plugin -> .opencode/plugins/astria.js');
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
messages.push('OpenCode plugin: already installed');
|
|
250
|
+
}
|
|
251
|
+
break;
|
|
252
|
+
}
|
|
253
|
+
if (cfg.mcp) {
|
|
254
|
+
const label = MCP_LABELS[cfg.mcp];
|
|
255
|
+
messages.push((0, settings_inject_1.injectAgentMcp)(projectDir, cfg.mcp)
|
|
256
|
+
? `${label.name} -> ${label.file}`
|
|
257
|
+
: `${label.name}: already registered`);
|
|
258
|
+
}
|
|
259
|
+
return messages;
|
|
260
|
+
}
|
|
261
|
+
function uninstallPlatform(platform, projectDir) {
|
|
262
|
+
const messages = [];
|
|
263
|
+
if (platform === 'cursor') {
|
|
264
|
+
if ((0, settings_inject_1.removeCursorRule)(projectDir)) {
|
|
265
|
+
messages.push('Cursor rule: removed');
|
|
266
|
+
}
|
|
267
|
+
else {
|
|
268
|
+
messages.push('Cursor rule: not found');
|
|
269
|
+
}
|
|
270
|
+
messages.push((0, settings_inject_1.removeAgentMcp)(projectDir, 'cursor')
|
|
271
|
+
? 'Cursor MCP server: removed'
|
|
272
|
+
: 'Cursor MCP server: not found');
|
|
273
|
+
return messages;
|
|
274
|
+
}
|
|
275
|
+
if (platform === 'kiro') {
|
|
276
|
+
const cfg = platforms_1.PLATFORMS.kiro;
|
|
277
|
+
if (cfg.skillFile) {
|
|
278
|
+
const homeDir = os.homedir();
|
|
279
|
+
const dst = path.join(homeDir, cfg.skillDst);
|
|
280
|
+
try {
|
|
281
|
+
fs.unlinkSync(dst);
|
|
282
|
+
messages.push(`Skill file removed: ${dst}`);
|
|
283
|
+
}
|
|
284
|
+
catch {
|
|
285
|
+
messages.push('Skill file: not found');
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
messages.push(...removeLegacySkillFile('kiro', cfg));
|
|
289
|
+
if ((0, settings_inject_1.removeKiroSteering)(projectDir)) {
|
|
290
|
+
messages.push('Kiro steering: removed');
|
|
291
|
+
}
|
|
292
|
+
else {
|
|
293
|
+
messages.push('Kiro steering: not found');
|
|
294
|
+
}
|
|
295
|
+
return messages;
|
|
296
|
+
}
|
|
297
|
+
const cfg = platforms_1.PLATFORMS[platform];
|
|
298
|
+
if (!cfg) {
|
|
299
|
+
messages.push(`Unknown platform: ${platform}`);
|
|
300
|
+
return messages;
|
|
301
|
+
}
|
|
302
|
+
if (cfg.skillFile) {
|
|
303
|
+
const homeDir = os.homedir();
|
|
304
|
+
let dst = path.join(homeDir, cfg.skillDst);
|
|
305
|
+
if (platform === 'claude') {
|
|
306
|
+
const configDir = process.env.CLAUDE_CONFIG_DIR;
|
|
307
|
+
if (configDir)
|
|
308
|
+
dst = path.join(configDir, 'skills', 'astria', 'SKILL.md');
|
|
309
|
+
}
|
|
310
|
+
try {
|
|
311
|
+
fs.unlinkSync(dst);
|
|
312
|
+
messages.push(`Skill file removed: ${dst}`);
|
|
313
|
+
}
|
|
314
|
+
catch {
|
|
315
|
+
messages.push('Skill file: not found');
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
messages.push(...removeLegacySkillFile(platform, cfg));
|
|
319
|
+
if (cfg.claudeMd) {
|
|
320
|
+
const claudeMdPath = path.join(os.homedir(), '.claude', 'CLAUDE.md');
|
|
321
|
+
(0, markdown_inject_1.removeSection)(claudeMdPath);
|
|
322
|
+
messages.push('User CLAUDE.md: astria section removed');
|
|
323
|
+
}
|
|
324
|
+
if (cfg.claudeMd) {
|
|
325
|
+
(0, markdown_inject_1.removeSection)(path.join(projectDir, 'CLAUDE.md'));
|
|
326
|
+
messages.push('Project CLAUDE.md: astria section removed');
|
|
327
|
+
}
|
|
328
|
+
if (cfg.agentsMd) {
|
|
329
|
+
(0, markdown_inject_1.removeSection)(path.join(projectDir, 'AGENTS.md'));
|
|
330
|
+
messages.push('Project AGENTS.md: astria section removed');
|
|
331
|
+
}
|
|
332
|
+
if (cfg.geminiMd) {
|
|
333
|
+
(0, markdown_inject_1.removeSection)(path.join(projectDir, 'GEMINI.md'));
|
|
334
|
+
messages.push('Project GEMINI.md: astria section removed');
|
|
335
|
+
}
|
|
336
|
+
switch (cfg.settingsHook) {
|
|
337
|
+
case 'claude':
|
|
338
|
+
(0, settings_inject_1.removeClaudeHook)(projectDir);
|
|
339
|
+
messages.push('Claude PreToolUse hook: removed');
|
|
340
|
+
break;
|
|
341
|
+
case 'codex':
|
|
342
|
+
(0, settings_inject_1.removeCodexHook)(projectDir);
|
|
343
|
+
messages.push('Codex PreToolUse hook: removed');
|
|
344
|
+
break;
|
|
345
|
+
case 'gemini':
|
|
346
|
+
(0, settings_inject_1.removeGeminiHook)(projectDir);
|
|
347
|
+
messages.push('Gemini BeforeTool hook: removed');
|
|
348
|
+
break;
|
|
349
|
+
case 'opencode':
|
|
350
|
+
(0, settings_inject_1.removeOpenCodePlugin)(projectDir);
|
|
351
|
+
messages.push('OpenCode plugin: removed');
|
|
352
|
+
break;
|
|
353
|
+
}
|
|
354
|
+
if (cfg.mcp) {
|
|
355
|
+
const label = MCP_LABELS[cfg.mcp];
|
|
356
|
+
messages.push((0, settings_inject_1.removeAgentMcp)(projectDir, cfg.mcp)
|
|
357
|
+
? `${label.name}: removed`
|
|
358
|
+
: `${label.name}: not found`);
|
|
359
|
+
}
|
|
360
|
+
return messages;
|
|
361
|
+
}
|
|
362
362
|
//# sourceMappingURL=index.js.map
|