auriga-cli 1.0.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/README.md +77 -0
- package/README.zh-CN.md +77 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +77 -0
- package/dist/plugins.d.ts +1 -0
- package/dist/plugins.js +112 -0
- package/dist/skills.d.ts +2 -0
- package/dist/skills.js +68 -0
- package/dist/utils.d.ts +44 -0
- package/dist/utils.js +230 -0
- package/dist/workflow.d.ts +1 -0
- package/dist/workflow.js +47 -0
- package/package.json +27 -0
package/README.md
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
English | [中文](README.zh-CN.md)
|
|
2
|
+
|
|
3
|
+
# auriga-cli
|
|
4
|
+
|
|
5
|
+
A modular Claude Code harness — install only the parts you need.
|
|
6
|
+
|
|
7
|
+
This repo itself is a fully configured harness project. You can clone it to see the full setup, or use the CLI to install individual modules into your own project.
|
|
8
|
+
|
|
9
|
+
## What's Included
|
|
10
|
+
|
|
11
|
+
| Module | Description |
|
|
12
|
+
|---|---|
|
|
13
|
+
| **Workflow** | `CLAUDE.md` development workflow: requirement clarification -> TDD -> Review, Harness principles, Subagent usage guide |
|
|
14
|
+
| **Skills** | Development process skills — brainstorming, systematic-debugging, TDD, verification, planning, playwright |
|
|
15
|
+
| **Plugins** | Recommended Claude Code plugins — skill-creator, claude-md-management, hookify, codex |
|
|
16
|
+
|
|
17
|
+
## Quick Start
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npx auriga-cli
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Interactive menu — select what to install:
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
? Select module types to install:
|
|
27
|
+
◉ Workflow — CLAUDE.md + AGENTS.md
|
|
28
|
+
◉ Skills — Development process skills
|
|
29
|
+
◉ Plugins — Claude Code plugins
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Each module supports scope selection (Skills: project/global, Plugins: user/project).
|
|
33
|
+
|
|
34
|
+
## Module Details
|
|
35
|
+
|
|
36
|
+
### Workflow
|
|
37
|
+
|
|
38
|
+
Copies `CLAUDE.md` to the target project and creates an `AGENTS.md` symlink for compatibility with different Agent frameworks. Supports English and Chinese — you choose during installation.
|
|
39
|
+
|
|
40
|
+
- Backs up existing `CLAUDE.md` before overwriting
|
|
41
|
+
- Covers: requirement clarification, TDD, code review, branch workflow, subagent orchestration
|
|
42
|
+
|
|
43
|
+
### Skills
|
|
44
|
+
|
|
45
|
+
Installs selected skills via `npx skills add`, targeting both Claude Code and Codex.
|
|
46
|
+
|
|
47
|
+
| Skill | Source | Description |
|
|
48
|
+
|---|---|---|
|
|
49
|
+
| brainstorming | [obra/superpowers](https://github.com/obra/superpowers) | Requirement clarification and design exploration |
|
|
50
|
+
| systematic-debugging | [obra/superpowers](https://github.com/obra/superpowers) | Systematic debugging — find root cause before fixing |
|
|
51
|
+
| test-driven-development | [obra/superpowers](https://github.com/obra/superpowers) | Test-driven development workflow |
|
|
52
|
+
| verification-before-completion | [obra/superpowers](https://github.com/obra/superpowers) | Pre-completion verification — evidence before assertions |
|
|
53
|
+
| planning-with-files | [OthmanAdi/planning-with-files](https://github.com/OthmanAdi/planning-with-files) | File-based task planning and progress tracking |
|
|
54
|
+
| playwright-cli | [microsoft/playwright-cli](https://github.com/microsoft/playwright-cli) | Browser automation and testing |
|
|
55
|
+
| ui-ux-pro-max | [nextlevelbuilder/ui-ux-pro-max-skill](https://github.com/nextlevelbuilder/ui-ux-pro-max-skill) | UI/UX design and development enhancement |
|
|
56
|
+
|
|
57
|
+
Supports both project and global installation scopes.
|
|
58
|
+
|
|
59
|
+
### Plugins
|
|
60
|
+
|
|
61
|
+
Installs selected plugins via `claude plugins install`, automatically adding required marketplaces.
|
|
62
|
+
|
|
63
|
+
| Plugin | Description |
|
|
64
|
+
|---|---|
|
|
65
|
+
| skill-creator | Create and manage custom skills |
|
|
66
|
+
| claude-md-management | Audit and improve CLAUDE.md |
|
|
67
|
+
| hookify | Create hooks from conversation analysis |
|
|
68
|
+
| codex | Codex cross-model collaboration |
|
|
69
|
+
|
|
70
|
+
## Requirements
|
|
71
|
+
|
|
72
|
+
- Node.js >= 18
|
|
73
|
+
- [Claude Code](https://docs.anthropic.com/en/docs/claude-code) (required for Plugins module)
|
|
74
|
+
|
|
75
|
+
## License
|
|
76
|
+
|
|
77
|
+
MIT
|
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
[English](README.md) | 中文
|
|
2
|
+
|
|
3
|
+
# auriga-cli
|
|
4
|
+
|
|
5
|
+
模块化的 Claude Code harness —— 按需选装你需要的部分。
|
|
6
|
+
|
|
7
|
+
这个仓库本身就是一个完整配置好的 harness 项目。可以直接 clone 查看完整配置,也可以用 CLI 把各模块安装到你自己的项目中。
|
|
8
|
+
|
|
9
|
+
## 包含什么
|
|
10
|
+
|
|
11
|
+
| 模块 | 说明 |
|
|
12
|
+
|---|---|
|
|
13
|
+
| **Workflow** | `CLAUDE.md` 开发工作流:需求澄清 → TDD → Review,Harness 原则,Subagent 使用指南 |
|
|
14
|
+
| **Skills** | 开发流程 skills —— brainstorming、systematic-debugging、TDD、verification、planning、playwright |
|
|
15
|
+
| **Plugins** | 推荐的 Claude Code 插件 —— skill-creator、claude-md-management、hookify、codex |
|
|
16
|
+
|
|
17
|
+
## 快速开始
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npx auriga-cli
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
交互式菜单,按需选择安装:
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
? 选择要安装的模块类型:
|
|
27
|
+
◉ Workflow — CLAUDE.md + AGENTS.md
|
|
28
|
+
◉ Skills — 开发流程 skills
|
|
29
|
+
◉ Plugins — Claude Code 插件
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
每个模块支持作用域选择(Skills: project/global,Plugins: user/project)。
|
|
33
|
+
|
|
34
|
+
## 模块详情
|
|
35
|
+
|
|
36
|
+
### Workflow
|
|
37
|
+
|
|
38
|
+
将 `CLAUDE.md` 复制到目标项目,并创建 `AGENTS.md` 软链接以兼容不同 Agent 框架。支持中英文版本,安装时可选择。
|
|
39
|
+
|
|
40
|
+
- 目标已有 `CLAUDE.md` 时会自动备份后覆盖
|
|
41
|
+
- 涵盖:需求澄清、TDD、代码 Review、分支工作流、Subagent 编排
|
|
42
|
+
|
|
43
|
+
### Skills
|
|
44
|
+
|
|
45
|
+
通过 `npx skills add` 逐个安装选中的 skills,同时安装到 Claude Code 和 Codex。
|
|
46
|
+
|
|
47
|
+
| Skill | 来源 | 说明 |
|
|
48
|
+
|---|---|---|
|
|
49
|
+
| brainstorming | [obra/superpowers](https://github.com/obra/superpowers) | 需求澄清与设计探索 |
|
|
50
|
+
| systematic-debugging | [obra/superpowers](https://github.com/obra/superpowers) | 系统化调试,先找根因再修复 |
|
|
51
|
+
| test-driven-development | [obra/superpowers](https://github.com/obra/superpowers) | 测试驱动开发流程 |
|
|
52
|
+
| verification-before-completion | [obra/superpowers](https://github.com/obra/superpowers) | 完成前验证,用证据说话 |
|
|
53
|
+
| planning-with-files | [OthmanAdi/planning-with-files](https://github.com/OthmanAdi/planning-with-files) | 文件化任务计划与进度跟踪 |
|
|
54
|
+
| playwright-cli | [microsoft/playwright-cli](https://github.com/microsoft/playwright-cli) | 浏览器自动化与测试 |
|
|
55
|
+
| ui-ux-pro-max | [nextlevelbuilder/ui-ux-pro-max-skill](https://github.com/nextlevelbuilder/ui-ux-pro-max-skill) | UI/UX 设计与开发增强 |
|
|
56
|
+
|
|
57
|
+
支持 project 和 global 两种安装范围。
|
|
58
|
+
|
|
59
|
+
### Plugins
|
|
60
|
+
|
|
61
|
+
通过 `claude plugins install` 安装选中的插件,自动添加所需的 marketplace。
|
|
62
|
+
|
|
63
|
+
| 插件 | 说明 |
|
|
64
|
+
|---|---|
|
|
65
|
+
| skill-creator | 创建和管理自定义 skills |
|
|
66
|
+
| claude-md-management | 审计和改进 CLAUDE.md |
|
|
67
|
+
| hookify | 从对话分析创建 hooks |
|
|
68
|
+
| codex | Codex 跨模型协作 |
|
|
69
|
+
|
|
70
|
+
## 环境要求
|
|
71
|
+
|
|
72
|
+
- Node.js >= 18
|
|
73
|
+
- [Claude Code](https://docs.anthropic.com/en/docs/claude-code)(Plugins 模块需要)
|
|
74
|
+
|
|
75
|
+
## License
|
|
76
|
+
|
|
77
|
+
MIT
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import { checkbox } from "@inquirer/prompts";
|
|
4
|
+
import { fetchContentRoot, printBanner, withEsc } from "./utils.js";
|
|
5
|
+
import { installWorkflow } from "./workflow.js";
|
|
6
|
+
import { installSkills, installRecommendedSkills } from "./skills.js";
|
|
7
|
+
import { installPlugins } from "./plugins.js";
|
|
8
|
+
const require = createRequire(import.meta.url);
|
|
9
|
+
const { version } = require("../package.json");
|
|
10
|
+
async function main() {
|
|
11
|
+
printBanner(version);
|
|
12
|
+
console.log("");
|
|
13
|
+
if (process.env.DEV === "1") {
|
|
14
|
+
console.log("Using local content (DEV mode)\n");
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
console.log("Fetching latest content from GitHub...");
|
|
18
|
+
}
|
|
19
|
+
const packageRoot = await fetchContentRoot();
|
|
20
|
+
if (process.env.DEV !== "1")
|
|
21
|
+
console.log("");
|
|
22
|
+
const moduleTypes = await withEsc(checkbox({
|
|
23
|
+
message: "Select module types to install:",
|
|
24
|
+
choices: [
|
|
25
|
+
{
|
|
26
|
+
name: "Workflow — CLAUDE.md + AGENTS.md",
|
|
27
|
+
value: "workflow",
|
|
28
|
+
checked: true,
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: "Skills — Development process skills (brainstorming, TDD, debugging...)",
|
|
32
|
+
value: "skills",
|
|
33
|
+
checked: true,
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: "Recommended Skills — Extra utility skills (claude-code-agent, codex-agent...)",
|
|
37
|
+
value: "recommended",
|
|
38
|
+
checked: true,
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: "Plugins — Claude Code plugins (skill-creator, hookify, codex...)",
|
|
42
|
+
value: "plugins",
|
|
43
|
+
checked: true,
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
}));
|
|
47
|
+
if (moduleTypes.length === 0) {
|
|
48
|
+
console.log("Nothing selected. Bye!");
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (moduleTypes.includes("workflow")) {
|
|
52
|
+
console.log("\n--- Workflow ---\n");
|
|
53
|
+
await installWorkflow(packageRoot);
|
|
54
|
+
}
|
|
55
|
+
if (moduleTypes.includes("skills")) {
|
|
56
|
+
console.log("\n--- Skills ---\n");
|
|
57
|
+
await installSkills(packageRoot);
|
|
58
|
+
}
|
|
59
|
+
if (moduleTypes.includes("recommended")) {
|
|
60
|
+
console.log("\n--- Recommended Skills ---\n");
|
|
61
|
+
await installRecommendedSkills(packageRoot);
|
|
62
|
+
}
|
|
63
|
+
if (moduleTypes.includes("plugins")) {
|
|
64
|
+
console.log("\n--- Plugins ---\n");
|
|
65
|
+
await installPlugins(packageRoot);
|
|
66
|
+
}
|
|
67
|
+
console.log("\n\u2728 Installation complete!\n");
|
|
68
|
+
}
|
|
69
|
+
main().catch((err) => {
|
|
70
|
+
if (err instanceof Error &&
|
|
71
|
+
["ExitPromptError", "CancelPromptError"].includes(err.name)) {
|
|
72
|
+
console.log("\nCancelled.");
|
|
73
|
+
process.exit(0);
|
|
74
|
+
}
|
|
75
|
+
console.error(err);
|
|
76
|
+
process.exit(1);
|
|
77
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function installPlugins(packageRoot: string): Promise<void>;
|
package/dist/plugins.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { checkbox, select } from "@inquirer/prompts";
|
|
4
|
+
import { exec, log, withEsc } from "./utils.js";
|
|
5
|
+
function getInstalledPlugins() {
|
|
6
|
+
try {
|
|
7
|
+
const output = exec("claude plugins list --json");
|
|
8
|
+
const plugins = JSON.parse(output);
|
|
9
|
+
const cwd = process.cwd();
|
|
10
|
+
const installed = new Map();
|
|
11
|
+
for (const p of plugins) {
|
|
12
|
+
// project scope 只匹配当前目录
|
|
13
|
+
if (p.scope === "project" && p.projectPath !== cwd)
|
|
14
|
+
continue;
|
|
15
|
+
const scopes = installed.get(p.id) || [];
|
|
16
|
+
scopes.push(p.scope);
|
|
17
|
+
installed.set(p.id, scopes);
|
|
18
|
+
}
|
|
19
|
+
return installed;
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return new Map();
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function getInstalledMarketplaces() {
|
|
26
|
+
try {
|
|
27
|
+
const output = exec("claude plugins marketplace list");
|
|
28
|
+
const names = new Set();
|
|
29
|
+
for (const match of output.matchAll(/❯\s+(\S+)/g)) {
|
|
30
|
+
names.add(match[1]);
|
|
31
|
+
}
|
|
32
|
+
return names;
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return new Set();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export async function installPlugins(packageRoot) {
|
|
39
|
+
// Check claude CLI availability
|
|
40
|
+
try {
|
|
41
|
+
exec("which claude");
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
log.error("'claude' CLI not found. Please install Claude Code first.");
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const configPath = path.join(packageRoot, ".claude", "plugins.json");
|
|
48
|
+
if (!fs.existsSync(configPath)) {
|
|
49
|
+
log.warn("No .claude/plugins.json found");
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
53
|
+
if (config.plugins.length === 0) {
|
|
54
|
+
log.warn("No plugins defined in plugins.json");
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const scope = await withEsc(select({
|
|
58
|
+
message: "Plugins installation scope:",
|
|
59
|
+
choices: [
|
|
60
|
+
{ name: "User (user-level)", value: "user" },
|
|
61
|
+
{ name: "Project (current project)", value: "project" },
|
|
62
|
+
],
|
|
63
|
+
}));
|
|
64
|
+
const installed = getInstalledPlugins();
|
|
65
|
+
const selected = await withEsc(checkbox({
|
|
66
|
+
message: "Select plugins to install:",
|
|
67
|
+
choices: config.plugins.map((p) => {
|
|
68
|
+
const scopes = installed.get(p.package);
|
|
69
|
+
const suffix = scopes ? ` (installed: ${scopes.join(", ")})` : "";
|
|
70
|
+
return {
|
|
71
|
+
name: `${p.name} — ${p.description}${suffix}`,
|
|
72
|
+
value: p,
|
|
73
|
+
checked: !scopes || !(scopes.includes("user") && scopes.includes("project")),
|
|
74
|
+
};
|
|
75
|
+
}),
|
|
76
|
+
}));
|
|
77
|
+
if (selected.length === 0) {
|
|
78
|
+
log.skip("No plugins selected");
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
// Install required marketplaces
|
|
82
|
+
const existingMarketplaces = getInstalledMarketplaces();
|
|
83
|
+
const marketplacesToAdd = new Map();
|
|
84
|
+
for (const plugin of selected) {
|
|
85
|
+
if (plugin.marketplace && !existingMarketplaces.has(plugin.marketplace.name)) {
|
|
86
|
+
marketplacesToAdd.set(plugin.marketplace.name, plugin.marketplace.source);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
for (const [name, source] of marketplacesToAdd) {
|
|
90
|
+
console.log(`\nAdding marketplace: ${name}...`);
|
|
91
|
+
try {
|
|
92
|
+
exec(`claude plugins marketplace add ${source}`, { inherit: true });
|
|
93
|
+
log.ok(`Marketplace ${name} added`);
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
log.error(`Failed to add marketplace: ${name}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// Install plugins
|
|
100
|
+
for (const plugin of selected) {
|
|
101
|
+
console.log(`\nInstalling ${plugin.name}...`);
|
|
102
|
+
try {
|
|
103
|
+
exec(`claude plugins install ${plugin.package} --scope ${scope}`, {
|
|
104
|
+
inherit: true,
|
|
105
|
+
});
|
|
106
|
+
log.ok(`${plugin.name} installed`);
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
log.error(`Failed to install: ${plugin.name}`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
package/dist/skills.d.ts
ADDED
package/dist/skills.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { checkbox, select } from "@inquirer/prompts";
|
|
4
|
+
import { exec, log, withEsc } from "./utils.js";
|
|
5
|
+
const WORKFLOW_SKILLS = [
|
|
6
|
+
"brainstorming",
|
|
7
|
+
"planning-with-files",
|
|
8
|
+
"playwright-cli",
|
|
9
|
+
"systematic-debugging",
|
|
10
|
+
"test-driven-development",
|
|
11
|
+
"ui-ux-pro-max",
|
|
12
|
+
"verification-before-completion",
|
|
13
|
+
];
|
|
14
|
+
const RECOMMENDED_DESCRIPTIONS = {
|
|
15
|
+
"claude-code-agent": "Delegate tasks to another Claude Code CLI instance",
|
|
16
|
+
"codex-agent": "Delegate tasks to Codex CLI",
|
|
17
|
+
};
|
|
18
|
+
function loadLock(packageRoot) {
|
|
19
|
+
return JSON.parse(fs.readFileSync(path.join(packageRoot, "skills-lock.json"), "utf-8"));
|
|
20
|
+
}
|
|
21
|
+
async function installSelected(entries, defaultChecked, descriptionMap) {
|
|
22
|
+
if (entries.length === 0) {
|
|
23
|
+
log.warn("No skills found");
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const scope = await withEsc(select({
|
|
27
|
+
message: "Skills installation scope:",
|
|
28
|
+
choices: [
|
|
29
|
+
{ name: "Project (current directory)", value: "project" },
|
|
30
|
+
{ name: "Global (user-level)", value: "global" },
|
|
31
|
+
],
|
|
32
|
+
}));
|
|
33
|
+
const selected = await withEsc(checkbox({
|
|
34
|
+
message: "Select skills to install:",
|
|
35
|
+
choices: entries.map(([name, entry]) => {
|
|
36
|
+
const desc = descriptionMap?.[name];
|
|
37
|
+
const label = desc ? `${name} — ${desc}` : `${name} (${entry.source})`;
|
|
38
|
+
return { name: label, value: name, checked: defaultChecked };
|
|
39
|
+
}),
|
|
40
|
+
}));
|
|
41
|
+
if (selected.length === 0) {
|
|
42
|
+
log.skip("No skills selected");
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const globalFlag = scope === "global" ? " -g" : "";
|
|
46
|
+
const lock = Object.fromEntries(entries);
|
|
47
|
+
for (const name of selected) {
|
|
48
|
+
const entry = lock[name];
|
|
49
|
+
console.log(`\nInstalling ${name}...`);
|
|
50
|
+
try {
|
|
51
|
+
exec(`npx skills add ${entry.source}${globalFlag} --skill ${name} --agent claude-code codex --yes`, { inherit: true });
|
|
52
|
+
log.ok(`${name}: installed`);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
log.error(`${name}: failed to install`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
export async function installSkills(packageRoot) {
|
|
60
|
+
const lock = loadLock(packageRoot);
|
|
61
|
+
const entries = Object.entries(lock.skills).filter(([name]) => WORKFLOW_SKILLS.includes(name));
|
|
62
|
+
await installSelected(entries, true);
|
|
63
|
+
}
|
|
64
|
+
export async function installRecommendedSkills(packageRoot) {
|
|
65
|
+
const lock = loadLock(packageRoot);
|
|
66
|
+
const entries = Object.entries(lock.skills).filter(([name]) => !WORKFLOW_SKILLS.includes(name));
|
|
67
|
+
await installSelected(entries, false, RECOMMENDED_DESCRIPTIONS);
|
|
68
|
+
}
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export interface SkillEntry {
|
|
2
|
+
source: string;
|
|
3
|
+
sourceType: string;
|
|
4
|
+
computedHash: string;
|
|
5
|
+
}
|
|
6
|
+
export interface SkillsLock {
|
|
7
|
+
version: number;
|
|
8
|
+
skills: Record<string, SkillEntry>;
|
|
9
|
+
}
|
|
10
|
+
export interface PluginDef {
|
|
11
|
+
name: string;
|
|
12
|
+
package: string;
|
|
13
|
+
description: string;
|
|
14
|
+
marketplace?: {
|
|
15
|
+
name: string;
|
|
16
|
+
source: string;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export interface PluginsConfig {
|
|
20
|
+
plugins: PluginDef[];
|
|
21
|
+
}
|
|
22
|
+
export declare function getPackageRoot(): string;
|
|
23
|
+
export declare function exec(cmd: string, opts?: {
|
|
24
|
+
cwd?: string;
|
|
25
|
+
inherit?: boolean;
|
|
26
|
+
}): string;
|
|
27
|
+
export interface LangOption {
|
|
28
|
+
value: string;
|
|
29
|
+
label: string;
|
|
30
|
+
file: string;
|
|
31
|
+
}
|
|
32
|
+
export declare const LANGUAGES: LangOption[];
|
|
33
|
+
export declare function fetchContentRoot(): Promise<string>;
|
|
34
|
+
export declare function fetchExtraContent(tmpDir: string, file: string): Promise<void>;
|
|
35
|
+
export declare function withEsc<T>(prompt: Promise<T> & {
|
|
36
|
+
cancel?: () => void;
|
|
37
|
+
}): Promise<T>;
|
|
38
|
+
export declare function printBanner(version: string): void;
|
|
39
|
+
export declare const log: {
|
|
40
|
+
ok: (msg: string) => void;
|
|
41
|
+
warn: (msg: string) => void;
|
|
42
|
+
error: (msg: string) => void;
|
|
43
|
+
skip: (msg: string) => void;
|
|
44
|
+
};
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import { execSync } from "node:child_process";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
// --- Package root ---
|
|
7
|
+
export function getPackageRoot() {
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
// dist/utils.js -> package root
|
|
10
|
+
return path.resolve(path.dirname(__filename), "..");
|
|
11
|
+
}
|
|
12
|
+
// --- Exec ---
|
|
13
|
+
export function exec(cmd, opts) {
|
|
14
|
+
return execSync(cmd, {
|
|
15
|
+
cwd: opts?.cwd,
|
|
16
|
+
stdio: opts?.inherit ? "inherit" : "pipe",
|
|
17
|
+
encoding: "utf-8",
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
export const LANGUAGES = [
|
|
21
|
+
{ value: "en", label: "English", file: "CLAUDE.md" },
|
|
22
|
+
{ value: "zh-CN", label: "中文", file: "CLAUDE.zh-CN.md" },
|
|
23
|
+
];
|
|
24
|
+
// --- Remote content ---
|
|
25
|
+
const REPO = "Ben2pc/auriga-cli";
|
|
26
|
+
const BRANCH = "main";
|
|
27
|
+
const CONTENT_FILES = [
|
|
28
|
+
"CLAUDE.md",
|
|
29
|
+
"skills-lock.json",
|
|
30
|
+
".claude/plugins.json",
|
|
31
|
+
];
|
|
32
|
+
async function fetchFile(file) {
|
|
33
|
+
const url = `https://raw.githubusercontent.com/${REPO}/${BRANCH}/${file}`;
|
|
34
|
+
const res = await fetch(url);
|
|
35
|
+
if (!res.ok)
|
|
36
|
+
throw new Error(`Failed to fetch ${url}: ${res.status}`);
|
|
37
|
+
return res.text();
|
|
38
|
+
}
|
|
39
|
+
export async function fetchContentRoot() {
|
|
40
|
+
if (process.env.DEV === "1") {
|
|
41
|
+
return getPackageRoot();
|
|
42
|
+
}
|
|
43
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "auriga-cli-"));
|
|
44
|
+
for (const file of CONTENT_FILES) {
|
|
45
|
+
const content = await fetchFile(file);
|
|
46
|
+
const dest = path.join(tmpDir, file);
|
|
47
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
48
|
+
fs.writeFileSync(dest, content);
|
|
49
|
+
}
|
|
50
|
+
return tmpDir;
|
|
51
|
+
}
|
|
52
|
+
export async function fetchExtraContent(tmpDir, file) {
|
|
53
|
+
const content = await fetchFile(file);
|
|
54
|
+
const dest = path.join(tmpDir, file);
|
|
55
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
56
|
+
fs.writeFileSync(dest, content);
|
|
57
|
+
}
|
|
58
|
+
// --- ESC support ---
|
|
59
|
+
export function withEsc(prompt) {
|
|
60
|
+
const onKeypress = (_, key) => {
|
|
61
|
+
if (key.name === "escape") {
|
|
62
|
+
prompt.cancel?.();
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
process.stdin.on("keypress", onKeypress);
|
|
66
|
+
return prompt.finally(() => {
|
|
67
|
+
process.stdin.removeListener("keypress", onKeypress);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
// --- ANSI ---
|
|
71
|
+
const reset = "\x1b[0m";
|
|
72
|
+
const green = "\x1b[32m";
|
|
73
|
+
const yellow = "\x1b[33m";
|
|
74
|
+
const red = "\x1b[31m";
|
|
75
|
+
const dim = "\x1b[2m";
|
|
76
|
+
// --- Banner ---
|
|
77
|
+
const ORIGINAL_ART = [
|
|
78
|
+
" ▄▀█ █ █ █▀█ █ █▀▀ ▄▀█",
|
|
79
|
+
" █▀█ █▄█ █▀▄ █ █▄█ █▀█",
|
|
80
|
+
];
|
|
81
|
+
// Winter Sky: #2C3E6B (靛蓝) → #5B7EA1 (钢蓝) → #D4A84B (暖金)
|
|
82
|
+
const GRADIENT_STOPS = [
|
|
83
|
+
[0x2C, 0x3E, 0x6B],
|
|
84
|
+
[0x5B, 0x7E, 0xA1],
|
|
85
|
+
[0xD4, 0xA8, 0x4B],
|
|
86
|
+
];
|
|
87
|
+
const SHADOW_COLOR = "\x1b[38;5;238m";
|
|
88
|
+
const SHADOW_DX = 1;
|
|
89
|
+
const SHADOW_DY = 1;
|
|
90
|
+
const SCALE = 2;
|
|
91
|
+
function decodeBanner(lines) {
|
|
92
|
+
const width = Math.max(...lines.map((l) => l.length));
|
|
93
|
+
const pixels = [];
|
|
94
|
+
for (const line of lines) {
|
|
95
|
+
const topRow = [];
|
|
96
|
+
const botRow = [];
|
|
97
|
+
for (let i = 0; i < width; i++) {
|
|
98
|
+
const ch = line[i] || " ";
|
|
99
|
+
if (ch === "█") {
|
|
100
|
+
topRow.push(1);
|
|
101
|
+
botRow.push(1);
|
|
102
|
+
}
|
|
103
|
+
else if (ch === "▀") {
|
|
104
|
+
topRow.push(1);
|
|
105
|
+
botRow.push(0);
|
|
106
|
+
}
|
|
107
|
+
else if (ch === "▄") {
|
|
108
|
+
topRow.push(0);
|
|
109
|
+
botRow.push(1);
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
topRow.push(0);
|
|
113
|
+
botRow.push(0);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
pixels.push(topRow, botRow);
|
|
117
|
+
}
|
|
118
|
+
return pixels;
|
|
119
|
+
}
|
|
120
|
+
function scaleBanner(pixels, n) {
|
|
121
|
+
const result = [];
|
|
122
|
+
for (const row of pixels) {
|
|
123
|
+
const scaledRow = row.flatMap((px) => Array(n).fill(px));
|
|
124
|
+
for (let i = 0; i < n; i++)
|
|
125
|
+
result.push([...scaledRow]);
|
|
126
|
+
}
|
|
127
|
+
return result;
|
|
128
|
+
}
|
|
129
|
+
function renderBannerWithShadow(pixels, dx, dy) {
|
|
130
|
+
const h = pixels.length;
|
|
131
|
+
const w = pixels[0].length;
|
|
132
|
+
// Build composite: 1=main, 2=shadow, 0=empty
|
|
133
|
+
const comp = pixels.map((r) => [...r]);
|
|
134
|
+
for (let i = 0; i < dy; i++)
|
|
135
|
+
comp.push(new Array(w + dx).fill(0));
|
|
136
|
+
for (const row of comp)
|
|
137
|
+
while (row.length < w + dx)
|
|
138
|
+
row.push(0);
|
|
139
|
+
for (let y = 0; y < h; y++) {
|
|
140
|
+
for (let x = 0; x < w; x++) {
|
|
141
|
+
if (pixels[y][x] === 1) {
|
|
142
|
+
const sy = y + dy, sx = x + dx;
|
|
143
|
+
if (sy < comp.length && sx < comp[0].length && comp[sy][sx] === 0) {
|
|
144
|
+
comp[sy][sx] = 2;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
// Render with per-character coloring
|
|
150
|
+
const totalW = comp[0].length;
|
|
151
|
+
const lines = [];
|
|
152
|
+
for (let y = 0; y < comp.length; y += 2) {
|
|
153
|
+
const top = comp[y];
|
|
154
|
+
const bot = y + 1 < comp.length ? comp[y + 1] : new Array(totalW).fill(0);
|
|
155
|
+
let line = "";
|
|
156
|
+
for (let x = 0; x < totalW; x++) {
|
|
157
|
+
const t = top[x], b = bot[x];
|
|
158
|
+
const tFill = t > 0, bFill = b > 0;
|
|
159
|
+
let ch;
|
|
160
|
+
if (tFill && bFill)
|
|
161
|
+
ch = "█";
|
|
162
|
+
else if (tFill && !bFill)
|
|
163
|
+
ch = "▀";
|
|
164
|
+
else if (!tFill && bFill)
|
|
165
|
+
ch = "▄";
|
|
166
|
+
else
|
|
167
|
+
ch = " ";
|
|
168
|
+
if (ch === " ") {
|
|
169
|
+
line += " ";
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
if (t === 1 || b === 1) {
|
|
173
|
+
const ratio = totalW <= 1 ? 0 : x / (totalW - 1);
|
|
174
|
+
const seg = ratio < 0.5 ? 0 : 1;
|
|
175
|
+
const localT = seg === 0 ? ratio * 2 : (ratio - 0.5) * 2;
|
|
176
|
+
const from = GRADIENT_STOPS[seg], to = GRADIENT_STOPS[seg + 1];
|
|
177
|
+
const r = Math.round(from[0] + localT * (to[0] - from[0]));
|
|
178
|
+
const g = Math.round(from[1] + localT * (to[1] - from[1]));
|
|
179
|
+
const bv = Math.round(from[2] + localT * (to[2] - from[2]));
|
|
180
|
+
line += `\x1b[38;2;${r};${g};${bv}m${ch}${reset}`;
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
line += `${SHADOW_COLOR}${ch}${reset}`;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
lines.push(line);
|
|
187
|
+
}
|
|
188
|
+
return lines.join("\n");
|
|
189
|
+
}
|
|
190
|
+
function renderBannerPlain(pixels) {
|
|
191
|
+
const lines = [];
|
|
192
|
+
for (let y = 0; y < pixels.length; y += 2) {
|
|
193
|
+
const top = pixels[y];
|
|
194
|
+
const bot = y + 1 < pixels.length ? pixels[y + 1] : top.map(() => 0);
|
|
195
|
+
let line = "";
|
|
196
|
+
for (let x = 0; x < top.length; x++) {
|
|
197
|
+
const t = top[x], b = bot[x];
|
|
198
|
+
if (t && b)
|
|
199
|
+
line += "█";
|
|
200
|
+
else if (t && !b)
|
|
201
|
+
line += "▀";
|
|
202
|
+
else if (!t && b)
|
|
203
|
+
line += "▄";
|
|
204
|
+
else
|
|
205
|
+
line += " ";
|
|
206
|
+
}
|
|
207
|
+
lines.push(line);
|
|
208
|
+
}
|
|
209
|
+
return lines.join("\n");
|
|
210
|
+
}
|
|
211
|
+
export function printBanner(version) {
|
|
212
|
+
const noColor = process.env.NO_COLOR !== undefined;
|
|
213
|
+
const pixels = scaleBanner(decodeBanner(ORIGINAL_ART), SCALE);
|
|
214
|
+
const art = noColor
|
|
215
|
+
? renderBannerPlain(pixels)
|
|
216
|
+
: renderBannerWithShadow(pixels, SHADOW_DX, SHADOW_DY);
|
|
217
|
+
const subtitle = noColor
|
|
218
|
+
? ` Claude Code Harness Installer v${version}`
|
|
219
|
+
: `${dim} Claude Code Harness Installer v${version}${reset}`;
|
|
220
|
+
console.log("");
|
|
221
|
+
console.log(art);
|
|
222
|
+
console.log(subtitle);
|
|
223
|
+
}
|
|
224
|
+
// --- Log ---
|
|
225
|
+
export const log = {
|
|
226
|
+
ok: (msg) => console.log(`${green}\u2713${reset} ${msg}`),
|
|
227
|
+
warn: (msg) => console.log(`${yellow}\u26a0${reset} ${msg}`),
|
|
228
|
+
error: (msg) => console.log(`${red}\u2717${reset} ${msg}`),
|
|
229
|
+
skip: (msg) => console.log(`${dim} skip: ${msg}${reset}`),
|
|
230
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function installWorkflow(packageRoot: string): Promise<void>;
|
package/dist/workflow.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { input, select } from "@inquirer/prompts";
|
|
4
|
+
import { LANGUAGES, fetchExtraContent, log, withEsc } from "./utils.js";
|
|
5
|
+
export async function installWorkflow(packageRoot) {
|
|
6
|
+
const lang = await withEsc(select({
|
|
7
|
+
message: "CLAUDE.md language:",
|
|
8
|
+
choices: LANGUAGES.map((l) => ({ name: l.label, value: l.value })),
|
|
9
|
+
default: "en",
|
|
10
|
+
}));
|
|
11
|
+
const targetDir = await withEsc(input({
|
|
12
|
+
message: "Workflow install target directory:",
|
|
13
|
+
default: process.cwd(),
|
|
14
|
+
}));
|
|
15
|
+
const resolved = path.resolve(targetDir);
|
|
16
|
+
if (!fs.existsSync(resolved) || !fs.statSync(resolved).isDirectory()) {
|
|
17
|
+
log.error(`Not a valid directory: ${resolved}`);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const langOpt = LANGUAGES.find((l) => l.value === lang);
|
|
21
|
+
// Lazy fetch: only download non-default language file when needed
|
|
22
|
+
if (langOpt.file !== "CLAUDE.md") {
|
|
23
|
+
console.log(`Fetching ${langOpt.label} template...`);
|
|
24
|
+
await fetchExtraContent(packageRoot, langOpt.file);
|
|
25
|
+
}
|
|
26
|
+
const sourceClaude = path.join(packageRoot, langOpt.file);
|
|
27
|
+
const targetClaude = path.join(resolved, "CLAUDE.md");
|
|
28
|
+
const targetAgents = path.join(resolved, "AGENTS.md");
|
|
29
|
+
// Copy CLAUDE.md
|
|
30
|
+
if (fs.existsSync(targetClaude)) {
|
|
31
|
+
const bakPath = targetClaude + ".bak";
|
|
32
|
+
fs.copyFileSync(targetClaude, bakPath);
|
|
33
|
+
log.warn(`Existing CLAUDE.md backed up to CLAUDE.md.bak`);
|
|
34
|
+
}
|
|
35
|
+
fs.copyFileSync(sourceClaude, targetClaude);
|
|
36
|
+
log.ok(`CLAUDE.md copied (${langOpt.label})`);
|
|
37
|
+
// Create AGENTS.md symlink
|
|
38
|
+
try {
|
|
39
|
+
fs.lstatSync(targetAgents);
|
|
40
|
+
fs.unlinkSync(targetAgents);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// does not exist, proceed
|
|
44
|
+
}
|
|
45
|
+
fs.symlinkSync("CLAUDE.md", targetAgents);
|
|
46
|
+
log.ok("AGENTS.md -> CLAUDE.md symlink created");
|
|
47
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "auriga-cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Interactive CLI to install Claude Code harness modules (Workflow, Skills, Plugins)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"auriga-cli": "dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsc",
|
|
14
|
+
"dev": "tsc --watch",
|
|
15
|
+
"start": "node dist/cli.js"
|
|
16
|
+
},
|
|
17
|
+
"engines": {
|
|
18
|
+
"node": ">=18"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@inquirer/prompts": "^8.0.0"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@types/node": "^22.0.0",
|
|
25
|
+
"typescript": "^5.7.0"
|
|
26
|
+
}
|
|
27
|
+
}
|