claudeinone-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 +72 -0
- package/dist/commands/doctor.js +84 -0
- package/dist/commands/init.js +105 -0
- package/dist/commands/new.js +26 -0
- package/dist/commands/uninstall.js +25 -0
- package/dist/commands/update.js +17 -0
- package/dist/commands/versions.js +36 -0
- package/dist/index.js +47 -0
- package/dist/utils/backup.js +31 -0
- package/dist/utils/installer.js +52 -0
- package/dist/utils/merger.js +56 -0
- package/dist/utils/platform.js +9 -0
- package/package.json +47 -0
package/README.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# claudeinone-cli
|
|
2
|
+
|
|
3
|
+
Production-grade framework for [Claude Code](https://claude.ai/code). Installs 213 skill guides, 37 specialist agents, and 95 `/co:` commands into any project — giving Claude expert-level knowledge across every major framework, pattern, and deployment strategy.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g claudeinone-cli
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# Install into an existing project
|
|
15
|
+
cd my-project
|
|
16
|
+
co init
|
|
17
|
+
|
|
18
|
+
# Create a new project
|
|
19
|
+
co new my-app
|
|
20
|
+
|
|
21
|
+
# Verify installation
|
|
22
|
+
co doctor
|
|
23
|
+
|
|
24
|
+
# Update to latest kit
|
|
25
|
+
co update
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## What Gets Installed
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
my-project/
|
|
32
|
+
├── CLAUDE.md # Master system prompt
|
|
33
|
+
├── .claude/
|
|
34
|
+
│ ├── skills/ # 213 production skill guides
|
|
35
|
+
│ ├── agents/ # 37 specialist agents
|
|
36
|
+
│ ├── commands/co/ # 95 /co: slash commands
|
|
37
|
+
│ ├── settings.json
|
|
38
|
+
│ └── .ck.json
|
|
39
|
+
├── docs/
|
|
40
|
+
├── plans/
|
|
41
|
+
└── journals/
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Commands in Claude Code
|
|
45
|
+
|
|
46
|
+
All ClaudeInOne commands are namespaced under `/co:` to avoid collision with Claude Code built-ins.
|
|
47
|
+
|
|
48
|
+
| Category | Commands |
|
|
49
|
+
|----------|----------|
|
|
50
|
+
| Plan | `/co:plan`, `/co:plan-fast`, `/co:plan-hard`, `/co:plan-parallel` |
|
|
51
|
+
| Build | `/co:cook`, `/co:bootstrap`, `/co:scaffold`, `/co:new-feature` |
|
|
52
|
+
| Fix | `/co:fix`, `/co:fix-fast`, `/co:fix-hard`, `/co:fix-types`, `/co:fix-ui` |
|
|
53
|
+
| Review | `/co:review-codebase`, `/co:review-security`, `/co:review-perf`, `/co:review-a11y` |
|
|
54
|
+
| Test | `/co:test`, `/co:test-gen`, `/co:test-ui` |
|
|
55
|
+
| Git | `/co:git-cm`, `/co:git-cp`, `/co:pr` |
|
|
56
|
+
| Deploy | `/co:docker`, `/co:k8s`, `/co:terraform`, `/co:deploy` |
|
|
57
|
+
| Docs | `/co:docs-init`, `/co:docs-update`, `/co:docs-readme` |
|
|
58
|
+
|
|
59
|
+
## CLI Reference
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
co init [--yes] [--global] [--exclude <pattern>]
|
|
63
|
+
co new <name> [--dir <path>]
|
|
64
|
+
co doctor [--fix] [--report]
|
|
65
|
+
co update
|
|
66
|
+
co versions
|
|
67
|
+
co uninstall [--global]
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## License
|
|
71
|
+
|
|
72
|
+
MIT
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { execFile } from "node:child_process";
|
|
4
|
+
import { promisify } from "node:util";
|
|
5
|
+
const execFileAsync = promisify(execFile);
|
|
6
|
+
async function exists(target) {
|
|
7
|
+
try {
|
|
8
|
+
await fs.access(target);
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export async function runDoctor(options) {
|
|
16
|
+
const cwd = process.cwd();
|
|
17
|
+
const checks = [];
|
|
18
|
+
const issues = [];
|
|
19
|
+
try {
|
|
20
|
+
await execFileAsync("claude", ["--version"], { timeout: 5_000 });
|
|
21
|
+
checks.push({ name: "claude executable", ok: true });
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
checks.push({ name: "claude executable", ok: false, details: "Claude Code CLI is not available in PATH." });
|
|
25
|
+
issues.push("Install Claude Code CLI and ensure `claude --version` works.");
|
|
26
|
+
}
|
|
27
|
+
const claudeDir = path.join(cwd, ".claude");
|
|
28
|
+
const hasClaudeDir = await exists(claudeDir);
|
|
29
|
+
checks.push({ name: ".claude directory", ok: hasClaudeDir });
|
|
30
|
+
const required = [
|
|
31
|
+
path.join(claudeDir, "commands"),
|
|
32
|
+
path.join(claudeDir, "agents"),
|
|
33
|
+
path.join(claudeDir, "skills"),
|
|
34
|
+
path.join(claudeDir, "settings.json"),
|
|
35
|
+
path.join(claudeDir, ".ck.json")
|
|
36
|
+
];
|
|
37
|
+
for (const req of required) {
|
|
38
|
+
checks.push({ name: path.relative(cwd, req), ok: await exists(req) });
|
|
39
|
+
}
|
|
40
|
+
const rootRequired = [
|
|
41
|
+
path.join(cwd, "CLAUDE.md"),
|
|
42
|
+
path.join(cwd, "docs"),
|
|
43
|
+
path.join(cwd, "plans"),
|
|
44
|
+
path.join(cwd, "journals")
|
|
45
|
+
];
|
|
46
|
+
for (const req of rootRequired) {
|
|
47
|
+
checks.push({ name: path.relative(cwd, req), ok: await exists(req) });
|
|
48
|
+
}
|
|
49
|
+
if (options.fix) {
|
|
50
|
+
await fs.mkdir(path.join(claudeDir, "commands"), { recursive: true });
|
|
51
|
+
await fs.mkdir(path.join(claudeDir, "agents"), { recursive: true });
|
|
52
|
+
await fs.mkdir(path.join(claudeDir, "skills"), { recursive: true });
|
|
53
|
+
await fs.mkdir(path.join(cwd, "docs"), { recursive: true });
|
|
54
|
+
await fs.mkdir(path.join(cwd, "plans"), { recursive: true });
|
|
55
|
+
await fs.mkdir(path.join(cwd, "journals"), { recursive: true });
|
|
56
|
+
const ckJson = path.join(claudeDir, ".ck.json");
|
|
57
|
+
if (!(await exists(ckJson))) {
|
|
58
|
+
await fs.writeFile(ckJson, JSON.stringify({ version: "1.0.0", kit: "claudeinone" }, null, 2) + "\n", "utf8");
|
|
59
|
+
}
|
|
60
|
+
const settings = path.join(claudeDir, "settings.json");
|
|
61
|
+
if (!(await exists(settings))) {
|
|
62
|
+
await fs.writeFile(settings, JSON.stringify({ hooks: { preTool: [], postTool: [] }, permissions: { default: "ask", allow: [] } }, null, 2) + "\n", "utf8");
|
|
63
|
+
}
|
|
64
|
+
const claudeMd = path.join(cwd, "CLAUDE.md");
|
|
65
|
+
if (!(await exists(claudeMd))) {
|
|
66
|
+
await fs.writeFile(claudeMd, "# ClaudeInOne\n", "utf8");
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const failed = checks.filter((c) => !c.ok);
|
|
70
|
+
if (options.report) {
|
|
71
|
+
console.log(JSON.stringify({ ok: failed.length === 0, checks, issues }, null, 2));
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
for (const check of checks) {
|
|
75
|
+
console.log(`${check.ok ? "OK" : "FAIL"} ${check.name}`);
|
|
76
|
+
}
|
|
77
|
+
if (failed.length > 0) {
|
|
78
|
+
console.log("Doctor found issues. Re-run with --fix to repair basic structure.");
|
|
79
|
+
process.exitCode = 1;
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
console.log("Doctor check passed.");
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import readline from "node:readline/promises";
|
|
5
|
+
import { stdin as input, stdout as output } from "node:process";
|
|
6
|
+
import { installKit } from "../utils/installer.js";
|
|
7
|
+
import { getFileDiffs } from "../utils/merger.js";
|
|
8
|
+
import { backupPaths } from "../utils/backup.js";
|
|
9
|
+
import { getGlobalClaudeDir } from "../utils/platform.js";
|
|
10
|
+
async function readKitVersion(kitRoot) {
|
|
11
|
+
const configPath = path.join(kitRoot, ".claude", ".ck.json");
|
|
12
|
+
const raw = await fs.readFile(configPath, "utf8");
|
|
13
|
+
const parsed = JSON.parse(raw);
|
|
14
|
+
return parsed.version ?? "1.0.0";
|
|
15
|
+
}
|
|
16
|
+
async function resolveConflictActions(conflicts) {
|
|
17
|
+
const skipPaths = new Set();
|
|
18
|
+
if (conflicts.length === 0)
|
|
19
|
+
return skipPaths;
|
|
20
|
+
const rl = readline.createInterface({ input, output });
|
|
21
|
+
let overwriteAll = false;
|
|
22
|
+
let skipAll = false;
|
|
23
|
+
try {
|
|
24
|
+
for (const rel of conflicts) {
|
|
25
|
+
if (overwriteAll)
|
|
26
|
+
continue;
|
|
27
|
+
if (skipAll) {
|
|
28
|
+
skipPaths.add(rel);
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
const answer = (await rl.question(`Conflict: ${rel}\nChoose [o]verwrite, [s]kip, overwrite [a]ll, skip a[l]l (default: s): `))
|
|
32
|
+
.trim()
|
|
33
|
+
.toLowerCase();
|
|
34
|
+
if (answer === "a") {
|
|
35
|
+
overwriteAll = true;
|
|
36
|
+
}
|
|
37
|
+
else if (answer === "l") {
|
|
38
|
+
skipAll = true;
|
|
39
|
+
skipPaths.add(rel);
|
|
40
|
+
}
|
|
41
|
+
else if (answer === "o") {
|
|
42
|
+
// overwrite this file
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
skipPaths.add(rel);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
rl.close();
|
|
51
|
+
}
|
|
52
|
+
return skipPaths;
|
|
53
|
+
}
|
|
54
|
+
export async function runInit(options) {
|
|
55
|
+
const filePath = fileURLToPath(import.meta.url);
|
|
56
|
+
const cliRoot = path.resolve(path.dirname(filePath), "..", "..");
|
|
57
|
+
const kitRoot = path.join(cliRoot, "kit");
|
|
58
|
+
const targetRoot = options.global ? getGlobalClaudeDir() : process.cwd();
|
|
59
|
+
if (options.global) {
|
|
60
|
+
const globalClaudeDir = getGlobalClaudeDir();
|
|
61
|
+
const diffs = await getFileDiffs(path.join(kitRoot, ".claude"), globalClaudeDir);
|
|
62
|
+
const conflicts = diffs.filter((d) => d.targetExists && !d.identical).map((d) => d.relPath);
|
|
63
|
+
let skipPaths = new Set();
|
|
64
|
+
if (conflicts.length > 0 && !options.yes) {
|
|
65
|
+
console.log(`Detected ${conflicts.length} changed global file conflicts.`);
|
|
66
|
+
skipPaths = await resolveConflictActions(conflicts);
|
|
67
|
+
}
|
|
68
|
+
if (conflicts.length > 0 && skipPaths.size < conflicts.length) {
|
|
69
|
+
const backup = await backupPaths(globalClaudeDir, ["."]);
|
|
70
|
+
if (backup)
|
|
71
|
+
console.log(`Backup created at ${backup}`);
|
|
72
|
+
}
|
|
73
|
+
await installKit({
|
|
74
|
+
kitRoot,
|
|
75
|
+
destinationRoot: globalClaudeDir,
|
|
76
|
+
sourceSubdir: ".claude",
|
|
77
|
+
extraExcludes: options.exclude ?? [],
|
|
78
|
+
skipPaths: [...skipPaths]
|
|
79
|
+
});
|
|
80
|
+
console.log(`Installed global kit at ${globalClaudeDir}`);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const diffs = await getFileDiffs(kitRoot, targetRoot);
|
|
84
|
+
const conflicts = diffs.filter((d) => d.targetExists && !d.identical).map((d) => d.relPath);
|
|
85
|
+
let skipPaths = new Set();
|
|
86
|
+
if (conflicts.length > 0 && !options.yes) {
|
|
87
|
+
console.log(`Detected ${conflicts.length} local file conflicts.`);
|
|
88
|
+
skipPaths = await resolveConflictActions(conflicts);
|
|
89
|
+
}
|
|
90
|
+
if (conflicts.length > 0 && skipPaths.size < conflicts.length) {
|
|
91
|
+
const backup = await backupPaths(targetRoot, [".claude", "CLAUDE.md", "docs", "plans", "journals"]);
|
|
92
|
+
if (backup)
|
|
93
|
+
console.log(`Backup created at ${backup}`);
|
|
94
|
+
}
|
|
95
|
+
await installKit({
|
|
96
|
+
kitRoot,
|
|
97
|
+
destinationRoot: targetRoot,
|
|
98
|
+
extraExcludes: options.exclude ?? [],
|
|
99
|
+
skipPaths: [...skipPaths]
|
|
100
|
+
});
|
|
101
|
+
const version = options.version ?? (await readKitVersion(kitRoot));
|
|
102
|
+
await fs.mkdir(path.join(targetRoot, ".claude"), { recursive: true });
|
|
103
|
+
await fs.writeFile(path.join(targetRoot, ".claude", ".ck.json"), JSON.stringify({ version, kit: options.kit }, null, 2) + "\n", "utf8");
|
|
104
|
+
console.log(`Installed ClaudeInOne (${options.kit}) v${version} in ${targetRoot}`);
|
|
105
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { execFile } from "node:child_process";
|
|
4
|
+
import { promisify } from "node:util";
|
|
5
|
+
import { runInit } from "./init.js";
|
|
6
|
+
const execFileAsync = promisify(execFile);
|
|
7
|
+
export async function runNew(name, options) {
|
|
8
|
+
const baseDir = options.dir ? path.resolve(options.dir) : process.cwd();
|
|
9
|
+
const projectDir = path.join(baseDir, name);
|
|
10
|
+
await fs.mkdir(projectDir, { recursive: true });
|
|
11
|
+
try {
|
|
12
|
+
await execFileAsync("git", ["init"], { cwd: projectDir });
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
// Git init is best-effort.
|
|
16
|
+
}
|
|
17
|
+
const previousCwd = process.cwd();
|
|
18
|
+
process.chdir(projectDir);
|
|
19
|
+
try {
|
|
20
|
+
await runInit({ kit: options.kit, yes: true });
|
|
21
|
+
}
|
|
22
|
+
finally {
|
|
23
|
+
process.chdir(previousCwd);
|
|
24
|
+
}
|
|
25
|
+
console.log(`Created new ClaudeInOne project at ${projectDir}`);
|
|
26
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { getGlobalClaudeDir } from "../utils/platform.js";
|
|
4
|
+
async function removeIfExists(target) {
|
|
5
|
+
await fs.rm(target, { recursive: true, force: true });
|
|
6
|
+
}
|
|
7
|
+
export async function runUninstall(options) {
|
|
8
|
+
const root = options.global ? getGlobalClaudeDir() : process.cwd();
|
|
9
|
+
if (options.global) {
|
|
10
|
+
await removeIfExists(path.join(root, "commands"));
|
|
11
|
+
await removeIfExists(path.join(root, "agents"));
|
|
12
|
+
await removeIfExists(path.join(root, "skills"));
|
|
13
|
+
await removeIfExists(path.join(root, "settings.json"));
|
|
14
|
+
await removeIfExists(path.join(root, ".ck.json"));
|
|
15
|
+
await removeIfExists(path.join(root, ".ckignore"));
|
|
16
|
+
console.log(`Removed ClaudeKit global files from ${root}`);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
await removeIfExists(path.join(root, ".claude"));
|
|
20
|
+
await removeIfExists(path.join(root, "CLAUDE.md"));
|
|
21
|
+
await removeIfExists(path.join(root, "docs"));
|
|
22
|
+
await removeIfExists(path.join(root, "plans"));
|
|
23
|
+
await removeIfExists(path.join(root, "journals"));
|
|
24
|
+
console.log(`Removed ClaudeKit files from ${root}`);
|
|
25
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
export async function runUpdate() {
|
|
3
|
+
const npmCommand = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
4
|
+
await new Promise((resolve, reject) => {
|
|
5
|
+
const child = spawn(npmCommand, ["install", "-g", "claudekit-cli@latest"], {
|
|
6
|
+
stdio: "inherit"
|
|
7
|
+
});
|
|
8
|
+
child.on("error", reject);
|
|
9
|
+
child.on("close", (code) => {
|
|
10
|
+
if (code === 0)
|
|
11
|
+
resolve();
|
|
12
|
+
else
|
|
13
|
+
reject(new Error(`npm exited with code ${code ?? "unknown"}`));
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
console.log("ClaudeKit CLI updated to latest.");
|
|
17
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { execFile } from "node:child_process";
|
|
5
|
+
import { promisify } from "node:util";
|
|
6
|
+
const execFileAsync = promisify(execFile);
|
|
7
|
+
async function readJson(filePath) {
|
|
8
|
+
try {
|
|
9
|
+
return JSON.parse(await fs.readFile(filePath, "utf8"));
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export async function runVersions() {
|
|
16
|
+
const filePath = fileURLToPath(import.meta.url);
|
|
17
|
+
const cliRoot = path.resolve(path.dirname(filePath), "..", "..");
|
|
18
|
+
const pkg = await readJson(path.join(cliRoot, "package.json"));
|
|
19
|
+
const kit = await readJson(path.join(cliRoot, "kit", ".claude", ".ck.json"));
|
|
20
|
+
console.log(`cli-installed: ${pkg?.version ?? "unknown"}`);
|
|
21
|
+
console.log(`kit-bundled: ${kit?.version ?? "unknown"}`);
|
|
22
|
+
try {
|
|
23
|
+
const { stdout } = await execFileAsync("npm", ["view", "claudeinone-cli", "versions", "--json"], { timeout: 10_000 });
|
|
24
|
+
const parsed = JSON.parse(stdout.trim());
|
|
25
|
+
const versions = Array.isArray(parsed) ? parsed : [parsed];
|
|
26
|
+
const latest = versions[versions.length - 1];
|
|
27
|
+
console.log(`registry-latest: ${latest}`);
|
|
28
|
+
console.log("registry-versions:");
|
|
29
|
+
for (const version of versions.slice(-15)) {
|
|
30
|
+
console.log(` - ${version}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
console.log("registry-latest: unavailable");
|
|
35
|
+
}
|
|
36
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import { runInit } from "./commands/init.js";
|
|
4
|
+
import { runNew } from "./commands/new.js";
|
|
5
|
+
import { runDoctor } from "./commands/doctor.js";
|
|
6
|
+
import { runVersions } from "./commands/versions.js";
|
|
7
|
+
import { runUpdate } from "./commands/update.js";
|
|
8
|
+
import { runUninstall } from "./commands/uninstall.js";
|
|
9
|
+
const program = new Command();
|
|
10
|
+
program.name("co").description("ClaudeInOne CLI").version("1.0.0");
|
|
11
|
+
program
|
|
12
|
+
.command("init")
|
|
13
|
+
.option("--kit <kit>", "kit name", "claudeinone")
|
|
14
|
+
.option("-g, --global", "install globally into ~/.claude")
|
|
15
|
+
.option("-y, --yes", "overwrite without interactive prompts")
|
|
16
|
+
.option("--exclude <pattern...>", "extra files/directories to exclude")
|
|
17
|
+
.option("--version <version>", "override kit version metadata")
|
|
18
|
+
.action(async (opts) => {
|
|
19
|
+
await runInit({
|
|
20
|
+
kit: opts.kit,
|
|
21
|
+
global: Boolean(opts.global),
|
|
22
|
+
yes: Boolean(opts.yes),
|
|
23
|
+
exclude: opts.exclude,
|
|
24
|
+
version: opts.version
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
program
|
|
28
|
+
.command("new <name>")
|
|
29
|
+
.option("--kit <kit>", "kit name", "claudeinone")
|
|
30
|
+
.option("--dir <path>", "base directory")
|
|
31
|
+
.action(async (name, opts) => {
|
|
32
|
+
await runNew(name, { kit: opts.kit, dir: opts.dir });
|
|
33
|
+
});
|
|
34
|
+
program
|
|
35
|
+
.command("doctor")
|
|
36
|
+
.option("--fix", "repair basic structure")
|
|
37
|
+
.option("--report", "emit JSON report")
|
|
38
|
+
.action(async (opts) => {
|
|
39
|
+
await runDoctor({ fix: Boolean(opts.fix), report: Boolean(opts.report) });
|
|
40
|
+
});
|
|
41
|
+
program.command("versions").action(async () => runVersions());
|
|
42
|
+
program.command("update").action(async () => runUpdate());
|
|
43
|
+
program
|
|
44
|
+
.command("uninstall")
|
|
45
|
+
.option("-g, --global", "remove global install")
|
|
46
|
+
.action(async (opts) => runUninstall({ global: Boolean(opts.global) }));
|
|
47
|
+
program.parseAsync(process.argv);
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
async function exists(target) {
|
|
4
|
+
try {
|
|
5
|
+
await fs.access(target);
|
|
6
|
+
return true;
|
|
7
|
+
}
|
|
8
|
+
catch {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export async function backupPaths(baseDir, relativePaths) {
|
|
13
|
+
const existing = [];
|
|
14
|
+
for (const rel of relativePaths) {
|
|
15
|
+
const full = path.join(baseDir, rel);
|
|
16
|
+
if (await exists(full))
|
|
17
|
+
existing.push(rel);
|
|
18
|
+
}
|
|
19
|
+
if (existing.length === 0)
|
|
20
|
+
return null;
|
|
21
|
+
const stamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
22
|
+
const backupDir = path.join(baseDir, `.claude.backup.${stamp}`);
|
|
23
|
+
await fs.mkdir(backupDir, { recursive: true });
|
|
24
|
+
for (const rel of existing) {
|
|
25
|
+
const src = path.join(baseDir, rel);
|
|
26
|
+
const dest = path.join(backupDir, rel);
|
|
27
|
+
await fs.mkdir(path.dirname(dest), { recursive: true });
|
|
28
|
+
await fs.cp(src, dest, { recursive: true, force: true });
|
|
29
|
+
}
|
|
30
|
+
return backupDir;
|
|
31
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
function normalizePatterns(patterns) {
|
|
4
|
+
return patterns.map((p) => p.trim()).filter(Boolean);
|
|
5
|
+
}
|
|
6
|
+
function shouldSkip(relPath, patterns) {
|
|
7
|
+
const p = relPath.replaceAll("\\", "/");
|
|
8
|
+
return patterns.some((pattern) => {
|
|
9
|
+
const v = pattern.replaceAll("\\", "/");
|
|
10
|
+
return p === v || p.startsWith(`${v}/`) || path.basename(p) === v;
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
async function readIgnoreFile(kitRoot) {
|
|
14
|
+
const ignorePath = path.join(kitRoot, ".claude", ".ckignore");
|
|
15
|
+
try {
|
|
16
|
+
const raw = await fs.readFile(ignorePath, "utf8");
|
|
17
|
+
return raw
|
|
18
|
+
.split(/\r?\n/)
|
|
19
|
+
.map((line) => line.trim())
|
|
20
|
+
.filter((line) => line && !line.startsWith("#"));
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return [];
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
async function copyRecursive(srcRoot, currentSrc, destRoot, patterns) {
|
|
27
|
+
const entries = await fs.readdir(currentSrc, { withFileTypes: true });
|
|
28
|
+
for (const entry of entries) {
|
|
29
|
+
const src = path.join(currentSrc, entry.name);
|
|
30
|
+
const rel = path.relative(srcRoot, src);
|
|
31
|
+
if (shouldSkip(rel, patterns))
|
|
32
|
+
continue;
|
|
33
|
+
const dest = path.join(destRoot, rel);
|
|
34
|
+
if (entry.isDirectory()) {
|
|
35
|
+
await fs.mkdir(dest, { recursive: true });
|
|
36
|
+
await copyRecursive(srcRoot, src, destRoot, patterns);
|
|
37
|
+
}
|
|
38
|
+
else if (entry.isFile() || entry.isSymbolicLink()) {
|
|
39
|
+
await fs.mkdir(path.dirname(dest), { recursive: true });
|
|
40
|
+
await fs.cp(src, dest, { force: true, dereference: false });
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export async function installKit(options) {
|
|
45
|
+
const sourceRoot = options.sourceSubdir
|
|
46
|
+
? path.join(options.kitRoot, options.sourceSubdir)
|
|
47
|
+
: options.kitRoot;
|
|
48
|
+
const ignores = await readIgnoreFile(options.kitRoot);
|
|
49
|
+
const patterns = normalizePatterns([...(options.extraExcludes ?? []), ...(options.skipPaths ?? []), ...ignores]);
|
|
50
|
+
await fs.mkdir(options.destinationRoot, { recursive: true });
|
|
51
|
+
await copyRecursive(sourceRoot, sourceRoot, options.destinationRoot, patterns);
|
|
52
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
async function collectFiles(base, cursor = "") {
|
|
4
|
+
const root = path.join(base, cursor);
|
|
5
|
+
const entries = await fs.readdir(root, { withFileTypes: true }).catch(() => []);
|
|
6
|
+
const out = [];
|
|
7
|
+
for (const entry of entries) {
|
|
8
|
+
const rel = path.join(cursor, entry.name);
|
|
9
|
+
const full = path.join(base, rel);
|
|
10
|
+
if (entry.isDirectory()) {
|
|
11
|
+
out.push(...(await collectFiles(base, rel)));
|
|
12
|
+
}
|
|
13
|
+
else if (entry.isFile()) {
|
|
14
|
+
out.push(rel);
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
const stat = await fs.stat(full);
|
|
18
|
+
if (stat.isFile())
|
|
19
|
+
out.push(rel);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return out;
|
|
23
|
+
}
|
|
24
|
+
async function isSameFile(sourcePath, targetPath) {
|
|
25
|
+
try {
|
|
26
|
+
const [a, b] = await Promise.all([fs.readFile(sourcePath), fs.readFile(targetPath)]);
|
|
27
|
+
return a.equals(b);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
export async function getFileDiffs(sourceRoot, targetRoot) {
|
|
34
|
+
const srcFiles = await collectFiles(sourceRoot);
|
|
35
|
+
const diffs = [];
|
|
36
|
+
for (const rel of srcFiles) {
|
|
37
|
+
const target = path.join(targetRoot, rel);
|
|
38
|
+
try {
|
|
39
|
+
const stat = await fs.stat(target);
|
|
40
|
+
if (!stat.isFile()) {
|
|
41
|
+
diffs.push({ relPath: rel, targetExists: true, identical: false });
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
const source = path.join(sourceRoot, rel);
|
|
45
|
+
diffs.push({ relPath: rel, targetExists: true, identical: await isSameFile(source, target) });
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
diffs.push({ relPath: rel, targetExists: false, identical: false });
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return diffs.sort((a, b) => a.relPath.localeCompare(b.relPath));
|
|
52
|
+
}
|
|
53
|
+
export async function findConflicts(sourceRoot, targetRoot) {
|
|
54
|
+
const diffs = await getFileDiffs(sourceRoot, targetRoot);
|
|
55
|
+
return diffs.filter((d) => d.targetExists && !d.identical).map((d) => d.relPath);
|
|
56
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
export function getGlobalClaudeDir() {
|
|
4
|
+
if (process.platform === "win32") {
|
|
5
|
+
const base = process.env.LOCALAPPDATA ?? path.join(os.homedir(), "AppData", "Local");
|
|
6
|
+
return path.join(base, ".claude");
|
|
7
|
+
}
|
|
8
|
+
return path.join(os.homedir(), ".claude");
|
|
9
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "claudeinone-cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Production-grade Claude Code framework — 213 skills, 37 agents, 95 /co: commands",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"co": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc -p tsconfig.json",
|
|
11
|
+
"smoke": "bash ./scripts/smoke.sh",
|
|
12
|
+
"dev": "bun run src/index.ts",
|
|
13
|
+
"start": "node dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": ">=18.0.0"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"claude",
|
|
20
|
+
"claude-code",
|
|
21
|
+
"ai",
|
|
22
|
+
"llm",
|
|
23
|
+
"developer-tools",
|
|
24
|
+
"cli",
|
|
25
|
+
"framework",
|
|
26
|
+
"skills",
|
|
27
|
+
"agents",
|
|
28
|
+
"commands"
|
|
29
|
+
],
|
|
30
|
+
"author": "Abhi Poluri",
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"homepage": "https://github.com/AbhiPoluri/ClaudeInOne",
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "https://github.com/AbhiPoluri/ClaudeInOne.git"
|
|
36
|
+
},
|
|
37
|
+
"bugs": {
|
|
38
|
+
"url": "https://github.com/AbhiPoluri/ClaudeInOne/issues"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"commander": "^12.1.0"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@types/node": "^22.7.6",
|
|
45
|
+
"typescript": "^5.6.3"
|
|
46
|
+
}
|
|
47
|
+
}
|