@sporesec/arcana 2.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/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +219 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/audit.d.ts +17 -0
- package/dist/commands/audit.d.ts.map +1 -0
- package/dist/commands/audit.js +157 -0
- package/dist/commands/audit.js.map +1 -0
- package/dist/commands/audit.test.d.ts +2 -0
- package/dist/commands/audit.test.d.ts.map +1 -0
- package/dist/commands/audit.test.js +217 -0
- package/dist/commands/audit.test.js.map +1 -0
- package/dist/commands/clean.d.ts +5 -0
- package/dist/commands/clean.d.ts.map +1 -0
- package/dist/commands/clean.js +125 -0
- package/dist/commands/clean.js.map +1 -0
- package/dist/commands/config.d.ts +4 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +135 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/create.d.ts +2 -0
- package/dist/commands/create.d.ts.map +1 -0
- package/dist/commands/create.js +100 -0
- package/dist/commands/create.js.map +1 -0
- package/dist/commands/doctor.d.ts +6 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +213 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/info.d.ts +5 -0
- package/dist/commands/info.d.ts.map +1 -0
- package/dist/commands/info.js +114 -0
- package/dist/commands/info.js.map +1 -0
- package/dist/commands/init.d.ts +13 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +216 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/install.d.ts +8 -0
- package/dist/commands/install.d.ts.map +1 -0
- package/dist/commands/install.js +404 -0
- package/dist/commands/install.js.map +1 -0
- package/dist/commands/list.d.ts +8 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +100 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/providers.d.ts +6 -0
- package/dist/commands/providers.d.ts.map +1 -0
- package/dist/commands/providers.js +103 -0
- package/dist/commands/providers.js.map +1 -0
- package/dist/commands/scan.d.ts +5 -0
- package/dist/commands/scan.d.ts.map +1 -0
- package/dist/commands/scan.js +110 -0
- package/dist/commands/scan.js.map +1 -0
- package/dist/commands/search.d.ts +6 -0
- package/dist/commands/search.d.ts.map +1 -0
- package/dist/commands/search.js +58 -0
- package/dist/commands/search.js.map +1 -0
- package/dist/commands/stats.d.ts +4 -0
- package/dist/commands/stats.d.ts.map +1 -0
- package/dist/commands/stats.js +143 -0
- package/dist/commands/stats.js.map +1 -0
- package/dist/commands/uninstall.d.ts +6 -0
- package/dist/commands/uninstall.d.ts.map +1 -0
- package/dist/commands/uninstall.js +163 -0
- package/dist/commands/uninstall.js.map +1 -0
- package/dist/commands/update.d.ts +7 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +348 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/commands/validate.d.ts +6 -0
- package/dist/commands/validate.d.ts.map +1 -0
- package/dist/commands/validate.js +140 -0
- package/dist/commands/validate.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +39 -0
- package/dist/index.js.map +1 -0
- package/dist/interactive.d.ts +2 -0
- package/dist/interactive.d.ts.map +1 -0
- package/dist/interactive.js +812 -0
- package/dist/interactive.js.map +1 -0
- package/dist/providers/arcana.d.ts +5 -0
- package/dist/providers/arcana.d.ts.map +1 -0
- package/dist/providers/arcana.js +11 -0
- package/dist/providers/arcana.js.map +1 -0
- package/dist/providers/base.d.ts +11 -0
- package/dist/providers/base.d.ts.map +1 -0
- package/dist/providers/base.js +10 -0
- package/dist/providers/base.js.map +1 -0
- package/dist/providers/github.d.ts +25 -0
- package/dist/providers/github.d.ts.map +1 -0
- package/dist/providers/github.js +146 -0
- package/dist/providers/github.js.map +1 -0
- package/dist/registry.d.ts +9 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +71 -0
- package/dist/registry.js.map +1 -0
- package/dist/types.d.ts +67 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/atomic.d.ts +2 -0
- package/dist/utils/atomic.d.ts.map +1 -0
- package/dist/utils/atomic.js +20 -0
- package/dist/utils/atomic.js.map +1 -0
- package/dist/utils/atomic.test.d.ts +2 -0
- package/dist/utils/atomic.test.d.ts.map +1 -0
- package/dist/utils/atomic.test.js +31 -0
- package/dist/utils/atomic.test.js.map +1 -0
- package/dist/utils/cache.d.ts +4 -0
- package/dist/utils/cache.d.ts.map +1 -0
- package/dist/utils/cache.js +47 -0
- package/dist/utils/cache.js.map +1 -0
- package/dist/utils/config.d.ts +6 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +90 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/config.test.d.ts +2 -0
- package/dist/utils/config.test.d.ts.map +1 -0
- package/dist/utils/config.test.js +38 -0
- package/dist/utils/config.test.js.map +1 -0
- package/dist/utils/errors.d.ts +6 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +11 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/frontmatter.d.ts +12 -0
- package/dist/utils/frontmatter.d.ts.map +1 -0
- package/dist/utils/frontmatter.js +172 -0
- package/dist/utils/frontmatter.js.map +1 -0
- package/dist/utils/frontmatter.test.d.ts +2 -0
- package/dist/utils/frontmatter.test.d.ts.map +1 -0
- package/dist/utils/frontmatter.test.js +152 -0
- package/dist/utils/frontmatter.test.js.map +1 -0
- package/dist/utils/fs.d.ts +16 -0
- package/dist/utils/fs.d.ts.map +1 -0
- package/dist/utils/fs.js +118 -0
- package/dist/utils/fs.js.map +1 -0
- package/dist/utils/fs.test.d.ts +2 -0
- package/dist/utils/fs.test.d.ts.map +1 -0
- package/dist/utils/fs.test.js +145 -0
- package/dist/utils/fs.test.js.map +1 -0
- package/dist/utils/help.d.ts +6 -0
- package/dist/utils/help.d.ts.map +1 -0
- package/dist/utils/help.js +117 -0
- package/dist/utils/help.js.map +1 -0
- package/dist/utils/help.test.d.ts +2 -0
- package/dist/utils/help.test.d.ts.map +1 -0
- package/dist/utils/help.test.js +66 -0
- package/dist/utils/help.test.js.map +1 -0
- package/dist/utils/history.d.ts +10 -0
- package/dist/utils/history.d.ts.map +1 -0
- package/dist/utils/history.js +58 -0
- package/dist/utils/history.js.map +1 -0
- package/dist/utils/http.d.ts +17 -0
- package/dist/utils/http.d.ts.map +1 -0
- package/dist/utils/http.js +165 -0
- package/dist/utils/http.js.map +1 -0
- package/dist/utils/http.test.d.ts +2 -0
- package/dist/utils/http.test.d.ts.map +1 -0
- package/dist/utils/http.test.js +55 -0
- package/dist/utils/http.test.js.map +1 -0
- package/dist/utils/parallel.d.ts +2 -0
- package/dist/utils/parallel.d.ts.map +1 -0
- package/dist/utils/parallel.js +17 -0
- package/dist/utils/parallel.js.map +1 -0
- package/dist/utils/scanner.d.ts +27 -0
- package/dist/utils/scanner.d.ts.map +1 -0
- package/dist/utils/scanner.js +195 -0
- package/dist/utils/scanner.js.map +1 -0
- package/dist/utils/ui.d.ts +27 -0
- package/dist/utils/ui.d.ts.map +1 -0
- package/dist/utils/ui.js +99 -0
- package/dist/utils/ui.js.map +1 -0
- package/dist/utils/ui.test.d.ts +2 -0
- package/dist/utils/ui.test.d.ts.map +1 -0
- package/dist/utils/ui.test.js +31 -0
- package/dist/utils/ui.test.js.map +1 -0
- package/dist/utils/validate.d.ts +2 -0
- package/dist/utils/validate.d.ts.map +1 -0
- package/dist/utils/validate.js +7 -0
- package/dist/utils/validate.js.map +1 -0
- package/package.json +62 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
|
2
|
+
import { join, basename, dirname } from "node:path";
|
|
3
|
+
import * as p from "@clack/prompts";
|
|
4
|
+
import chalk from "chalk";
|
|
5
|
+
import { renderBanner } from "../utils/help.js";
|
|
6
|
+
export function detectProject(cwd) {
|
|
7
|
+
const name = basename(cwd);
|
|
8
|
+
if (existsSync(join(cwd, "go.mod")))
|
|
9
|
+
return { name, type: "Go", lang: "go" };
|
|
10
|
+
if (existsSync(join(cwd, "Cargo.toml")))
|
|
11
|
+
return { name, type: "Rust", lang: "rust" };
|
|
12
|
+
if (existsSync(join(cwd, "requirements.txt")) || existsSync(join(cwd, "pyproject.toml")))
|
|
13
|
+
return { name, type: "Python", lang: "python" };
|
|
14
|
+
if (existsSync(join(cwd, "package.json"))) {
|
|
15
|
+
try {
|
|
16
|
+
const raw = readFileSync(join(cwd, "package.json"), "utf-8");
|
|
17
|
+
const pkg = JSON.parse(raw);
|
|
18
|
+
if (pkg.dependencies?.next || pkg.devDependencies?.next)
|
|
19
|
+
return { name, type: "Next.js", lang: "typescript" };
|
|
20
|
+
if (pkg.dependencies?.react || pkg.devDependencies?.react)
|
|
21
|
+
return { name, type: "React", lang: "typescript" };
|
|
22
|
+
}
|
|
23
|
+
catch { /* ignore */ }
|
|
24
|
+
return { name, type: "Node.js", lang: "typescript" };
|
|
25
|
+
}
|
|
26
|
+
return { name, type: "Unknown", lang: "general" };
|
|
27
|
+
}
|
|
28
|
+
function claudeTemplate(proj) {
|
|
29
|
+
return `# CLAUDE.md - ${proj.name}
|
|
30
|
+
|
|
31
|
+
## Project
|
|
32
|
+
- **Type:** ${proj.type}
|
|
33
|
+
- **Language:** ${proj.lang}
|
|
34
|
+
|
|
35
|
+
## Coding Preferences
|
|
36
|
+
- Follow existing patterns in the codebase
|
|
37
|
+
- Handle errors explicitly
|
|
38
|
+
- Use meaningful variable names, no abbreviations
|
|
39
|
+
|
|
40
|
+
## Build & Test
|
|
41
|
+
<!-- Add build/test commands so Claude can verify changes -->
|
|
42
|
+
<!-- Example: npm test, go test ./..., pytest -->
|
|
43
|
+
|
|
44
|
+
## Project Structure
|
|
45
|
+
<!-- Describe your project structure here -->
|
|
46
|
+
`;
|
|
47
|
+
}
|
|
48
|
+
function cursorTemplate(proj) {
|
|
49
|
+
return `---
|
|
50
|
+
description: Project conventions for ${proj.name}
|
|
51
|
+
globs:
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
# ${proj.name} (${proj.type})
|
|
55
|
+
|
|
56
|
+
## Language
|
|
57
|
+
${proj.lang}
|
|
58
|
+
|
|
59
|
+
## Coding Standards
|
|
60
|
+
- Follow existing patterns in the codebase
|
|
61
|
+
- Handle errors explicitly
|
|
62
|
+
- Use meaningful variable names
|
|
63
|
+
`;
|
|
64
|
+
}
|
|
65
|
+
function codexTemplate(proj) {
|
|
66
|
+
return `# AGENTS.md - ${proj.name}
|
|
67
|
+
|
|
68
|
+
## Project
|
|
69
|
+
Type: ${proj.type} | Language: ${proj.lang}
|
|
70
|
+
|
|
71
|
+
## Sandbox
|
|
72
|
+
Codex runs in a sandboxed environment with no network access.
|
|
73
|
+
All dependencies must be pre-installed before the session.
|
|
74
|
+
|
|
75
|
+
## Guidelines
|
|
76
|
+
- Follow existing patterns in the codebase
|
|
77
|
+
- Handle errors explicitly
|
|
78
|
+
- Use meaningful variable names
|
|
79
|
+
`;
|
|
80
|
+
}
|
|
81
|
+
function geminiTemplate(proj) {
|
|
82
|
+
return `# GEMINI.md - ${proj.name}
|
|
83
|
+
|
|
84
|
+
## Project Context
|
|
85
|
+
This is a ${proj.type} project using ${proj.lang}.
|
|
86
|
+
|
|
87
|
+
## Project Files
|
|
88
|
+
<!-- List key files and directories so Gemini can navigate the codebase -->
|
|
89
|
+
<!-- Example: src/ - main source, tests/ - test files -->
|
|
90
|
+
|
|
91
|
+
## Instructions
|
|
92
|
+
- Follow existing patterns in the codebase
|
|
93
|
+
- Handle errors explicitly
|
|
94
|
+
- Use meaningful variable names
|
|
95
|
+
`;
|
|
96
|
+
}
|
|
97
|
+
function antigravityTemplate(proj) {
|
|
98
|
+
return `# Antigravity - ${proj.name}
|
|
99
|
+
|
|
100
|
+
## Project Context
|
|
101
|
+
This is a ${proj.type} project using ${proj.lang}.
|
|
102
|
+
Antigravity workspace: \`.agent/\` (rules, workflows, skills)
|
|
103
|
+
|
|
104
|
+
## Project Files
|
|
105
|
+
<!-- List key files and directories so the agent can navigate the codebase -->
|
|
106
|
+
<!-- Example: src/ - main source, tests/ - test files -->
|
|
107
|
+
|
|
108
|
+
## Instructions
|
|
109
|
+
- Follow existing patterns in the codebase
|
|
110
|
+
- Handle errors explicitly
|
|
111
|
+
- Use meaningful variable names
|
|
112
|
+
`;
|
|
113
|
+
}
|
|
114
|
+
function windsurfTemplate(proj) {
|
|
115
|
+
return `# Windsurf Cascades Rules - ${proj.name}
|
|
116
|
+
|
|
117
|
+
Project: ${proj.name} (${proj.type})
|
|
118
|
+
Language: ${proj.lang}
|
|
119
|
+
|
|
120
|
+
## Rules
|
|
121
|
+
- Follow existing patterns in the codebase
|
|
122
|
+
- Handle errors explicitly
|
|
123
|
+
- Use meaningful variable names
|
|
124
|
+
- Always explain changes before applying them
|
|
125
|
+
`;
|
|
126
|
+
}
|
|
127
|
+
function aiderTemplate(_proj) {
|
|
128
|
+
return `# Aider Configuration
|
|
129
|
+
model: sonnet
|
|
130
|
+
auto-commits: true
|
|
131
|
+
auto-test: false
|
|
132
|
+
# Add conventions below
|
|
133
|
+
conventions:
|
|
134
|
+
- Follow existing patterns in the codebase
|
|
135
|
+
- Write clean, maintainable code
|
|
136
|
+
- Handle errors explicitly
|
|
137
|
+
`;
|
|
138
|
+
}
|
|
139
|
+
const TOOL_FILES = {
|
|
140
|
+
claude: { path: "CLAUDE.md", template: claudeTemplate, label: "Claude Code" },
|
|
141
|
+
cursor: {
|
|
142
|
+
path: join(".cursor", "rules", "project.mdc"),
|
|
143
|
+
template: cursorTemplate,
|
|
144
|
+
label: "Cursor",
|
|
145
|
+
},
|
|
146
|
+
codex: { path: "AGENTS.md", template: codexTemplate, label: "Codex CLI" },
|
|
147
|
+
gemini: { path: "GEMINI.md", template: geminiTemplate, label: "Gemini CLI" },
|
|
148
|
+
antigravity: { path: "GEMINI.md", template: antigravityTemplate, label: "Antigravity" },
|
|
149
|
+
windsurf: { path: ".windsurfrules", template: windsurfTemplate, label: "Windsurf" },
|
|
150
|
+
aider: { path: ".aider.conf.yml", template: aiderTemplate, label: "Aider" },
|
|
151
|
+
};
|
|
152
|
+
export const SKILL_SUGGESTIONS = {
|
|
153
|
+
"Go": ["golang-pro", "go-linter-configuration", "testing-strategy", "security-review"],
|
|
154
|
+
"Rust": ["rust-best-practices", "testing-strategy", "security-review"],
|
|
155
|
+
"Python": ["python-best-practices", "testing-strategy", "security-review"],
|
|
156
|
+
"Next.js": ["typescript", "typescript-advanced", "frontend-design", "performance-optimization", "security-review"],
|
|
157
|
+
"React": ["typescript", "frontend-design", "frontend-code-review", "testing-strategy"],
|
|
158
|
+
"Node.js": ["typescript", "npm-package", "testing-strategy", "security-review"],
|
|
159
|
+
};
|
|
160
|
+
export const SKILL_SUGGESTIONS_DEFAULT = ["code-reviewer", "security-review", "codebase-dissection", "testing-strategy"];
|
|
161
|
+
export async function initCommand(opts) {
|
|
162
|
+
console.log(renderBanner());
|
|
163
|
+
console.log();
|
|
164
|
+
p.intro(chalk.bold("Initialize arcana"));
|
|
165
|
+
const cwd = process.cwd();
|
|
166
|
+
const proj = detectProject(cwd);
|
|
167
|
+
p.log.step(`Project detected: ${chalk.cyan(proj.name)} (${proj.type})`);
|
|
168
|
+
if (opts.tool && opts.tool !== "all" && !(opts.tool in TOOL_FILES)) {
|
|
169
|
+
const valid = Object.keys(TOOL_FILES).join(", ");
|
|
170
|
+
p.cancel(`Unknown tool: ${opts.tool}. Valid: ${valid}`);
|
|
171
|
+
process.exit(1);
|
|
172
|
+
}
|
|
173
|
+
const tools = opts.tool === "all" || !opts.tool
|
|
174
|
+
? ["claude", "cursor", "codex", "gemini", "antigravity", "windsurf", "aider"]
|
|
175
|
+
: [opts.tool];
|
|
176
|
+
let created = 0;
|
|
177
|
+
let skipped = 0;
|
|
178
|
+
for (const tool of tools) {
|
|
179
|
+
const entry = TOOL_FILES[tool];
|
|
180
|
+
if (!entry) {
|
|
181
|
+
p.log.warn(`Unknown tool: ${tool}`);
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
const relPath = typeof entry.path === "function" ? entry.path(cwd) : entry.path;
|
|
185
|
+
const fullPath = join(cwd, relPath);
|
|
186
|
+
if (existsSync(fullPath)) {
|
|
187
|
+
p.log.info(`Skip ${relPath} (already exists)`);
|
|
188
|
+
skipped++;
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
const content = entry.template(proj);
|
|
192
|
+
try {
|
|
193
|
+
mkdirSync(dirname(fullPath), { recursive: true });
|
|
194
|
+
writeFileSync(fullPath, content, "utf-8");
|
|
195
|
+
}
|
|
196
|
+
catch (err) {
|
|
197
|
+
p.log.warn(`Failed to create ${relPath}: ${err instanceof Error ? err.message : "unknown error"}`);
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
p.log.success(`Created ${chalk.cyan(relPath)} (${entry.label})`);
|
|
201
|
+
created++;
|
|
202
|
+
}
|
|
203
|
+
if (created > 0) {
|
|
204
|
+
p.log.info(`${created} file${created > 1 ? "s" : ""} created. Edit them to match your project.`);
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
p.log.info("All config files already exist.");
|
|
208
|
+
}
|
|
209
|
+
if (skipped > 0)
|
|
210
|
+
p.log.info(`${skipped} skipped (already exist)`);
|
|
211
|
+
const suggestions = SKILL_SUGGESTIONS[proj.type] || SKILL_SUGGESTIONS_DEFAULT;
|
|
212
|
+
const skillList = suggestions.map((s) => `arcana install ${s}`).join("\n");
|
|
213
|
+
p.note(skillList, "Recommended skills");
|
|
214
|
+
p.outro(`Next: ${chalk.cyan("arcana install <skill>")} or ${chalk.cyan("arcana install --all")}`);
|
|
215
|
+
}
|
|
216
|
+
//# sourceMappingURL=init.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../src/commands/install.ts"],"names":[],"mappings":"AAiDA,wBAAsB,cAAc,CAClC,UAAU,EAAE,MAAM,EAAE,EACpB,IAAI,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,GAC5F,OAAO,CAAC,IAAI,CAAC,CA+Bf"}
|
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import { printErrorWithHint } from "../utils/ui.js";
|
|
4
|
+
import { installSkill, isSkillInstalled, writeSkillMeta, readSkillMeta } from "../utils/fs.js";
|
|
5
|
+
import { getProvider, getProviders } from "../registry.js";
|
|
6
|
+
import { loadConfig } from "../utils/config.js";
|
|
7
|
+
import { renderBanner } from "../utils/help.js";
|
|
8
|
+
import { validateSlug } from "../utils/validate.js";
|
|
9
|
+
import { scanSkillContent } from "../utils/scanner.js";
|
|
10
|
+
/**
|
|
11
|
+
* Scan fetched skill files for security threats before installing.
|
|
12
|
+
* Returns true if install should proceed, false to block.
|
|
13
|
+
*/
|
|
14
|
+
function preInstallScan(skillName, files, force) {
|
|
15
|
+
const skillMd = files.find(f => f.path.endsWith("SKILL.md"));
|
|
16
|
+
if (!skillMd)
|
|
17
|
+
return true;
|
|
18
|
+
const issues = scanSkillContent(skillMd.content);
|
|
19
|
+
if (issues.length === 0)
|
|
20
|
+
return true;
|
|
21
|
+
const critical = issues.filter(i => i.level === "critical");
|
|
22
|
+
const high = issues.filter(i => i.level === "high");
|
|
23
|
+
if (critical.length > 0) {
|
|
24
|
+
p.log.error(`Security scan blocked ${chalk.bold(skillName)}:`);
|
|
25
|
+
for (const issue of critical) {
|
|
26
|
+
p.log.error(` [CRIT] ${issue.category}: ${issue.detail} (line ${issue.line})`);
|
|
27
|
+
}
|
|
28
|
+
for (const issue of high) {
|
|
29
|
+
p.log.warn(` [HIGH] ${issue.category}: ${issue.detail} (line ${issue.line})`);
|
|
30
|
+
}
|
|
31
|
+
if (!force) {
|
|
32
|
+
p.log.info(chalk.dim("Use --force to install anyway (not recommended)."));
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
p.log.warn("Installing despite security issues (--force).");
|
|
36
|
+
}
|
|
37
|
+
else if (high.length > 0) {
|
|
38
|
+
p.log.warn(`Security warnings for ${chalk.bold(skillName)}:`);
|
|
39
|
+
for (const issue of high) {
|
|
40
|
+
p.log.warn(` [HIGH] ${issue.category}: ${issue.detail} (line ${issue.line})`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
export async function installCommand(skillNames, opts) {
|
|
46
|
+
if (opts.json) {
|
|
47
|
+
return installJson(skillNames, opts);
|
|
48
|
+
}
|
|
49
|
+
console.log(renderBanner());
|
|
50
|
+
console.log();
|
|
51
|
+
if (skillNames.length === 0 && !opts.all) {
|
|
52
|
+
p.intro(chalk.bold("Install skill"));
|
|
53
|
+
p.cancel("Specify a skill name or use --all");
|
|
54
|
+
p.log.info("Usage: arcana install <skill-name> [skill2 ...]");
|
|
55
|
+
p.log.info(" arcana install --all");
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
const providerName = opts.provider ?? loadConfig().defaultProvider;
|
|
59
|
+
const providers = opts.all ? getProviders() : [getProvider(providerName)];
|
|
60
|
+
if (providers.length === 0) {
|
|
61
|
+
p.cancel("No providers configured. Run: arcana providers --add owner/repo");
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
if (opts.all) {
|
|
65
|
+
await installAllInteractive(providers, opts.dryRun, opts.force);
|
|
66
|
+
}
|
|
67
|
+
else if (skillNames.length === 1) {
|
|
68
|
+
await installOneInteractive(skillNames[0], providers[0], opts.dryRun, opts.force);
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
await installMultipleInteractive(skillNames, providers[0], opts.dryRun, opts.force);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
async function installOneInteractive(skillName, provider, dryRun, force) {
|
|
75
|
+
p.intro(chalk.bold("Install skill"));
|
|
76
|
+
try {
|
|
77
|
+
validateSlug(skillName, "skill name");
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
p.cancel(err instanceof Error ? err.message : "Invalid skill name");
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
if (isSkillInstalled(skillName)) {
|
|
84
|
+
if (dryRun) {
|
|
85
|
+
p.log.info(`${skillName} is already installed.`);
|
|
86
|
+
p.outro("Dry run complete.");
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (!force) {
|
|
90
|
+
p.cancel(`${skillName} is already installed. Use --force to reinstall.`);
|
|
91
|
+
process.exit(0);
|
|
92
|
+
}
|
|
93
|
+
const existingMeta = readSkillMeta(skillName);
|
|
94
|
+
if (existingMeta?.source && existingMeta.source !== provider.name) {
|
|
95
|
+
p.log.warn(`Overwriting ${skillName} (was from ${existingMeta.source}, now from ${provider.name})`);
|
|
96
|
+
}
|
|
97
|
+
p.log.warn(`${skillName} is already installed. Reinstalling...`);
|
|
98
|
+
}
|
|
99
|
+
if (dryRun) {
|
|
100
|
+
p.log.info(`Would install ${chalk.bold(skillName)} from ${provider.name}`);
|
|
101
|
+
p.outro("Dry run complete.");
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
const spin = p.spinner();
|
|
105
|
+
spin.start(`Fetching ${chalk.bold(skillName)} from ${provider.name}...`);
|
|
106
|
+
try {
|
|
107
|
+
const files = await provider.fetch(skillName);
|
|
108
|
+
spin.stop("Fetched.");
|
|
109
|
+
if (!preInstallScan(skillName, files, force)) {
|
|
110
|
+
process.exit(1);
|
|
111
|
+
}
|
|
112
|
+
const spin2 = p.spinner();
|
|
113
|
+
spin2.start(`Installing ${chalk.bold(skillName)}...`);
|
|
114
|
+
const dir = installSkill(skillName, files);
|
|
115
|
+
const remote = await provider.info(skillName);
|
|
116
|
+
writeSkillMeta(skillName, {
|
|
117
|
+
version: remote?.version ?? "0.0.0",
|
|
118
|
+
installedAt: new Date().toISOString(),
|
|
119
|
+
source: provider.name,
|
|
120
|
+
description: remote?.description,
|
|
121
|
+
fileCount: files.length,
|
|
122
|
+
sizeBytes: files.reduce((s, f) => s + f.content.length, 0),
|
|
123
|
+
});
|
|
124
|
+
const sizeKB = files.reduce((s, f) => s + f.content.length, 0) / 1024;
|
|
125
|
+
spin2.stop(`Installed ${chalk.bold(skillName)} (${files.length} files, ${sizeKB.toFixed(1)} KB)`);
|
|
126
|
+
if (sizeKB > 50) {
|
|
127
|
+
p.log.warn(`Large skill (${sizeKB.toFixed(0)} KB, ~${Math.round(sizeKB * 3)} tokens). May use significant context.`);
|
|
128
|
+
}
|
|
129
|
+
p.log.info(`Location: ${dir}`);
|
|
130
|
+
p.outro(`Next: ${chalk.cyan("arcana validate " + skillName)}`);
|
|
131
|
+
}
|
|
132
|
+
catch (err) {
|
|
133
|
+
p.log.error(`Failed to install ${skillName}`);
|
|
134
|
+
printErrorWithHint(err, true);
|
|
135
|
+
process.exit(1);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
async function installMultipleInteractive(skillNames, provider, dryRun, force) {
|
|
139
|
+
p.intro(chalk.bold(`Install ${skillNames.length} skills`));
|
|
140
|
+
for (const name of skillNames) {
|
|
141
|
+
try {
|
|
142
|
+
validateSlug(name, "skill name");
|
|
143
|
+
}
|
|
144
|
+
catch (err) {
|
|
145
|
+
p.log.error(err instanceof Error ? err.message : `Invalid skill name: ${name}`);
|
|
146
|
+
process.exit(1);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (dryRun) {
|
|
150
|
+
const wouldInstall = [];
|
|
151
|
+
const alreadyInstalled = [];
|
|
152
|
+
for (const skillName of skillNames) {
|
|
153
|
+
if (isSkillInstalled(skillName) && !force) {
|
|
154
|
+
alreadyInstalled.push(skillName);
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
wouldInstall.push(skillName);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (wouldInstall.length > 0) {
|
|
161
|
+
p.log.info(`Would install: ${wouldInstall.join(", ")}`);
|
|
162
|
+
}
|
|
163
|
+
if (alreadyInstalled.length > 0) {
|
|
164
|
+
p.log.info(`Already installed: ${alreadyInstalled.join(", ")}`);
|
|
165
|
+
}
|
|
166
|
+
p.outro("Dry run complete.");
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
const spin = p.spinner();
|
|
170
|
+
spin.start(`Processing ${skillNames.length} skills...`);
|
|
171
|
+
const installedList = [];
|
|
172
|
+
const skippedList = [];
|
|
173
|
+
const failedList = [];
|
|
174
|
+
for (let i = 0; i < skillNames.length; i++) {
|
|
175
|
+
const skillName = skillNames[i];
|
|
176
|
+
if (isSkillInstalled(skillName) && !force) {
|
|
177
|
+
skippedList.push(skillName);
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
spin.message(`Installing ${chalk.bold(skillName)} (${i + 1}/${skillNames.length}) from ${provider.name}...`);
|
|
181
|
+
try {
|
|
182
|
+
const files = await provider.fetch(skillName);
|
|
183
|
+
if (!preInstallScan(skillName, files, force)) {
|
|
184
|
+
failedList.push(skillName);
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
installSkill(skillName, files);
|
|
188
|
+
const remote = await provider.info(skillName);
|
|
189
|
+
writeSkillMeta(skillName, {
|
|
190
|
+
version: remote?.version ?? "0.0.0",
|
|
191
|
+
installedAt: new Date().toISOString(),
|
|
192
|
+
source: provider.name,
|
|
193
|
+
description: remote?.description,
|
|
194
|
+
fileCount: files.length,
|
|
195
|
+
sizeBytes: files.reduce((s, f) => s + f.content.length, 0),
|
|
196
|
+
});
|
|
197
|
+
installedList.push(skillName);
|
|
198
|
+
}
|
|
199
|
+
catch (err) {
|
|
200
|
+
failedList.push(skillName);
|
|
201
|
+
if (err instanceof Error)
|
|
202
|
+
p.log.warn(`Failed to install ${skillName}: ${err.message}`);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
spin.stop(`Done`);
|
|
206
|
+
p.log.info(`${installedList.length} installed${skippedList.length > 0 ? `, ${skippedList.length} skipped (already installed)` : ""}${failedList.length > 0 ? `, ${failedList.length} failed` : ""}`);
|
|
207
|
+
p.outro(`Next: ${chalk.cyan("arcana doctor")}`);
|
|
208
|
+
if (failedList.length > 0)
|
|
209
|
+
process.exit(1);
|
|
210
|
+
}
|
|
211
|
+
async function installAllInteractive(providers, dryRun, force) {
|
|
212
|
+
p.intro(chalk.bold("Install all skills"));
|
|
213
|
+
const spin = p.spinner();
|
|
214
|
+
spin.start("Fetching skill list...");
|
|
215
|
+
if (dryRun) {
|
|
216
|
+
let total = 0;
|
|
217
|
+
for (const provider of providers) {
|
|
218
|
+
try {
|
|
219
|
+
const skills = await provider.list();
|
|
220
|
+
total += skills.length;
|
|
221
|
+
}
|
|
222
|
+
catch (err) {
|
|
223
|
+
if (err instanceof Error)
|
|
224
|
+
p.log.warn(`Failed to list ${provider.name}: ${err.message}`);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
spin.stop(`Would install ${total} skills`);
|
|
228
|
+
p.outro("Dry run complete.");
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
const installedList = [];
|
|
232
|
+
const skippedList = [];
|
|
233
|
+
const failedList = [];
|
|
234
|
+
for (const provider of providers) {
|
|
235
|
+
let skills;
|
|
236
|
+
try {
|
|
237
|
+
skills = await provider.list();
|
|
238
|
+
}
|
|
239
|
+
catch (err) {
|
|
240
|
+
if (err instanceof Error)
|
|
241
|
+
p.log.warn(`Failed to list ${provider.name}: ${err.message}`);
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
const total = skills.length;
|
|
245
|
+
for (let i = 0; i < total; i++) {
|
|
246
|
+
const skill = skills[i];
|
|
247
|
+
if (isSkillInstalled(skill.name) && !force) {
|
|
248
|
+
skippedList.push(skill.name);
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
try {
|
|
252
|
+
spin.message(`Installing ${chalk.bold(skill.name)} (${i + 1}/${total}) from ${provider.name}...`);
|
|
253
|
+
const files = await provider.fetch(skill.name);
|
|
254
|
+
if (!preInstallScan(skill.name, files, force)) {
|
|
255
|
+
failedList.push(skill.name);
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
installSkill(skill.name, files);
|
|
259
|
+
writeSkillMeta(skill.name, {
|
|
260
|
+
version: skill.version,
|
|
261
|
+
installedAt: new Date().toISOString(),
|
|
262
|
+
source: provider.name,
|
|
263
|
+
description: skill.description,
|
|
264
|
+
fileCount: files.length,
|
|
265
|
+
sizeBytes: files.reduce((s, f) => s + f.content.length, 0),
|
|
266
|
+
});
|
|
267
|
+
installedList.push(skill.name);
|
|
268
|
+
}
|
|
269
|
+
catch (err) {
|
|
270
|
+
failedList.push(skill.name);
|
|
271
|
+
if (err instanceof Error)
|
|
272
|
+
p.log.warn(`Failed to install ${skill.name}: ${err.message}`);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
spin.stop(`Installed ${installedList.length} skills${failedList.length > 0 ? `, ${failedList.length} failed` : ""}`);
|
|
277
|
+
if (skippedList.length > 0) {
|
|
278
|
+
p.log.info(`Skipped ${skippedList.length} already installed${force ? "" : " (use --force to reinstall)"}`);
|
|
279
|
+
}
|
|
280
|
+
p.outro(`Next: ${chalk.cyan("arcana doctor")}`);
|
|
281
|
+
if (failedList.length > 0)
|
|
282
|
+
process.exit(1);
|
|
283
|
+
}
|
|
284
|
+
async function installJson(skillNames, opts) {
|
|
285
|
+
if (skillNames.length === 0 && !opts.all) {
|
|
286
|
+
console.log(JSON.stringify({ installed: [], skipped: [], failed: [], error: "No skill specified" }));
|
|
287
|
+
process.exit(1);
|
|
288
|
+
}
|
|
289
|
+
const providerName = opts.provider ?? loadConfig().defaultProvider;
|
|
290
|
+
const providers = opts.all ? getProviders() : [getProvider(providerName)];
|
|
291
|
+
if (opts.all) {
|
|
292
|
+
if (opts.dryRun) {
|
|
293
|
+
const wouldInstall = [];
|
|
294
|
+
const errors = [];
|
|
295
|
+
for (const provider of providers) {
|
|
296
|
+
try {
|
|
297
|
+
const skills = await provider.list();
|
|
298
|
+
wouldInstall.push(...skills.map(s => s.name));
|
|
299
|
+
}
|
|
300
|
+
catch (err) {
|
|
301
|
+
errors.push(`Failed to list ${provider.name}: ${err instanceof Error ? err.message : "unknown error"}`);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
const result = { dryRun: true, wouldInstall };
|
|
305
|
+
if (errors.length > 0)
|
|
306
|
+
result.errors = errors;
|
|
307
|
+
console.log(JSON.stringify(result));
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
const installedList = [];
|
|
311
|
+
const skippedList = [];
|
|
312
|
+
const failedList = [];
|
|
313
|
+
const failedErrors = {};
|
|
314
|
+
const errors = [];
|
|
315
|
+
for (const provider of providers) {
|
|
316
|
+
let skills;
|
|
317
|
+
try {
|
|
318
|
+
skills = await provider.list();
|
|
319
|
+
}
|
|
320
|
+
catch (err) {
|
|
321
|
+
errors.push(`Failed to list ${provider.name}: ${err instanceof Error ? err.message : "unknown error"}`);
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
324
|
+
for (const skill of skills) {
|
|
325
|
+
if (isSkillInstalled(skill.name) && !opts.force) {
|
|
326
|
+
skippedList.push(skill.name);
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
try {
|
|
330
|
+
const files = await provider.fetch(skill.name);
|
|
331
|
+
if (!preInstallScan(skill.name, files, opts.force)) {
|
|
332
|
+
failedList.push(skill.name);
|
|
333
|
+
failedErrors[skill.name] = "Blocked by security scan";
|
|
334
|
+
continue;
|
|
335
|
+
}
|
|
336
|
+
installSkill(skill.name, files);
|
|
337
|
+
writeSkillMeta(skill.name, {
|
|
338
|
+
version: skill.version,
|
|
339
|
+
installedAt: new Date().toISOString(),
|
|
340
|
+
source: provider.name,
|
|
341
|
+
description: skill.description,
|
|
342
|
+
fileCount: files.length,
|
|
343
|
+
sizeBytes: files.reduce((s, f) => s + f.content.length, 0),
|
|
344
|
+
});
|
|
345
|
+
installedList.push(skill.name);
|
|
346
|
+
}
|
|
347
|
+
catch (err) {
|
|
348
|
+
failedList.push(skill.name);
|
|
349
|
+
failedErrors[skill.name] = err instanceof Error ? err.message : "unknown";
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
const result = { installed: installedList, skipped: skippedList, failed: failedList };
|
|
354
|
+
if (errors.length > 0)
|
|
355
|
+
result.errors = errors;
|
|
356
|
+
if (Object.keys(failedErrors).length > 0)
|
|
357
|
+
result.failedErrors = failedErrors;
|
|
358
|
+
console.log(JSON.stringify(result));
|
|
359
|
+
if (failedList.length > 0)
|
|
360
|
+
process.exit(1);
|
|
361
|
+
}
|
|
362
|
+
else {
|
|
363
|
+
const provider = providers[0];
|
|
364
|
+
if (opts.dryRun) {
|
|
365
|
+
console.log(JSON.stringify({ dryRun: true, wouldInstall: skillNames }));
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
const installedList = [];
|
|
369
|
+
const skippedList = [];
|
|
370
|
+
const failedList = [];
|
|
371
|
+
for (const skillName of skillNames) {
|
|
372
|
+
try {
|
|
373
|
+
validateSlug(skillName, "skill name");
|
|
374
|
+
if (isSkillInstalled(skillName) && !opts.force) {
|
|
375
|
+
skippedList.push(skillName);
|
|
376
|
+
continue;
|
|
377
|
+
}
|
|
378
|
+
const files = await provider.fetch(skillName);
|
|
379
|
+
if (!preInstallScan(skillName, files, opts.force)) {
|
|
380
|
+
failedList.push(skillName);
|
|
381
|
+
continue;
|
|
382
|
+
}
|
|
383
|
+
installSkill(skillName, files);
|
|
384
|
+
const remote = await provider.info(skillName);
|
|
385
|
+
writeSkillMeta(skillName, {
|
|
386
|
+
version: remote?.version ?? "0.0.0",
|
|
387
|
+
installedAt: new Date().toISOString(),
|
|
388
|
+
source: provider.name,
|
|
389
|
+
description: remote?.description,
|
|
390
|
+
fileCount: files.length,
|
|
391
|
+
sizeBytes: files.reduce((s, f) => s + f.content.length, 0),
|
|
392
|
+
});
|
|
393
|
+
installedList.push(skillName);
|
|
394
|
+
}
|
|
395
|
+
catch (err) {
|
|
396
|
+
failedList.push(skillName);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
console.log(JSON.stringify({ installed: installedList, skipped: skippedList, failed: failedList }));
|
|
400
|
+
if (failedList.length > 0)
|
|
401
|
+
process.exit(1);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
//# sourceMappingURL=install.js.map
|