@9000ai/cli 0.5.1 → 0.5.3
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/CHANGELOG.md +7 -1
- package/dist/commands/init.js +58 -29
- package/dist/commands/update.d.ts +2 -0
- package/dist/commands/update.js +84 -0
- package/dist/index.js +3 -1
- package/dist/postinstall.d.ts +1 -1
- package/dist/postinstall.js +50 -5
- package/package.json +1 -1
- package/skills/9000AI-hub/SKILL.md +16 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
## What's new in v0.5.
|
|
1
|
+
## What's new in v0.5.3
|
|
2
|
+
|
|
3
|
+
- `9000ai update` — sync skills after npm update, shows what changed
|
|
4
|
+
- `9000ai init --force` — smart diff, only updates changed system files
|
|
5
|
+
- User data (profile/, claims/, inbox/, projects/) is never overwritten
|
|
6
|
+
|
|
7
|
+
## v0.5.0
|
|
2
8
|
|
|
3
9
|
- `9000ai init --path <dir>` — one command to setup workspace
|
|
4
10
|
- `9000ai search keyword --wait` — auto-poll, returns results directly
|
package/dist/commands/init.js
CHANGED
|
@@ -1,14 +1,24 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, copyFileSync, readdirSync, statSync } from "fs";
|
|
1
|
+
import { existsSync, mkdirSync, copyFileSync, readFileSync, readdirSync, statSync } from "fs";
|
|
2
2
|
import { homedir } from "os";
|
|
3
3
|
import { join, relative } from "path";
|
|
4
4
|
const TEMPLATES_DIR = join(homedir(), ".9000ai", "skills", "9000AI-hub", "init", "templates");
|
|
5
|
-
/**
|
|
6
|
-
const
|
|
7
|
-
function
|
|
5
|
+
/** User data directories — never overwrite even with --force */
|
|
6
|
+
const USER_DATA_DIRS = ["profile", "claims", "inbox", "projects"];
|
|
7
|
+
function filesEqual(a, b) {
|
|
8
|
+
try {
|
|
9
|
+
return readFileSync(a).equals(readFileSync(b));
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function copyTemplates(src, dest, relBase, force) {
|
|
8
16
|
const created = [];
|
|
17
|
+
const updated = [];
|
|
9
18
|
const skipped = [];
|
|
19
|
+
const outdated = [];
|
|
10
20
|
if (!existsSync(src))
|
|
11
|
-
return { created, skipped };
|
|
21
|
+
return { created, updated, skipped, outdated };
|
|
12
22
|
const entries = readdirSync(src);
|
|
13
23
|
for (const entry of entries) {
|
|
14
24
|
const srcPath = join(src, entry);
|
|
@@ -19,35 +29,47 @@ function copyTemplates(src, dest, relBase) {
|
|
|
19
29
|
if (!existsSync(destPath)) {
|
|
20
30
|
mkdirSync(destPath, { recursive: true });
|
|
21
31
|
}
|
|
22
|
-
const sub = copyTemplates(srcPath, destPath, relBase);
|
|
32
|
+
const sub = copyTemplates(srcPath, destPath, relBase, force);
|
|
23
33
|
created.push(...sub.created);
|
|
34
|
+
updated.push(...sub.updated);
|
|
24
35
|
skipped.push(...sub.skipped);
|
|
36
|
+
outdated.push(...sub.outdated);
|
|
25
37
|
}
|
|
26
38
|
else {
|
|
27
|
-
if (existsSync(destPath)) {
|
|
28
|
-
// Never overwrite profile files
|
|
29
|
-
const topDir = rel.split(/[/\\]/)[0];
|
|
30
|
-
if (NEVER_OVERWRITE_DIRS.includes(topDir)) {
|
|
31
|
-
skipped.push(rel);
|
|
32
|
-
continue;
|
|
33
|
-
}
|
|
34
|
-
// Other existing files: also skip (idempotent)
|
|
35
|
-
skipped.push(rel);
|
|
36
|
-
}
|
|
37
|
-
else {
|
|
39
|
+
if (!existsSync(destPath)) {
|
|
38
40
|
mkdirSync(join(destPath, ".."), { recursive: true });
|
|
39
41
|
copyFileSync(srcPath, destPath);
|
|
40
42
|
created.push(rel);
|
|
41
43
|
}
|
|
44
|
+
else if (filesEqual(srcPath, destPath)) {
|
|
45
|
+
// identical, skip silently
|
|
46
|
+
skipped.push(rel);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
// content differs
|
|
50
|
+
const topDir = rel.split(/[/\\]/)[0];
|
|
51
|
+
if (USER_DATA_DIRS.includes(topDir)) {
|
|
52
|
+
// user data, never touch
|
|
53
|
+
skipped.push(rel);
|
|
54
|
+
}
|
|
55
|
+
else if (force) {
|
|
56
|
+
copyFileSync(srcPath, destPath);
|
|
57
|
+
updated.push(rel);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
outdated.push(rel);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
42
63
|
}
|
|
43
64
|
}
|
|
44
|
-
return { created, skipped };
|
|
65
|
+
return { created, updated, skipped, outdated };
|
|
45
66
|
}
|
|
46
67
|
export function registerInitCommand(program) {
|
|
47
68
|
program
|
|
48
69
|
.command("init")
|
|
49
|
-
.description("Initialize content workspace
|
|
70
|
+
.description("Initialize or update content workspace")
|
|
50
71
|
.requiredOption("--path <dir>", "Project directory to initialize")
|
|
72
|
+
.option("--force", "Update existing files (except profile/) to latest version")
|
|
51
73
|
.action((opts) => {
|
|
52
74
|
const dest = opts.path;
|
|
53
75
|
// Check templates exist
|
|
@@ -60,24 +82,31 @@ export function registerInitCommand(program) {
|
|
|
60
82
|
if (!existsSync(dest)) {
|
|
61
83
|
mkdirSync(dest, { recursive: true });
|
|
62
84
|
}
|
|
63
|
-
const { created, skipped } = copyTemplates(TEMPLATES_DIR, dest, dest);
|
|
85
|
+
const { created, updated, skipped, outdated } = copyTemplates(TEMPLATES_DIR, dest, dest, !!opts.force);
|
|
64
86
|
// Output summary
|
|
65
87
|
if (created.length > 0) {
|
|
66
88
|
console.log(`Created ${created.length} files:`);
|
|
67
89
|
created.forEach((f) => console.log(` + ${f}`));
|
|
68
90
|
}
|
|
69
|
-
if (
|
|
70
|
-
console.log(`
|
|
71
|
-
|
|
91
|
+
if (updated.length > 0) {
|
|
92
|
+
console.log(`Updated ${updated.length} files:`);
|
|
93
|
+
updated.forEach((f) => console.log(` ~ ${f}`));
|
|
72
94
|
}
|
|
73
|
-
if (
|
|
74
|
-
console.log(
|
|
95
|
+
if (outdated.length > 0) {
|
|
96
|
+
console.log(`\n${outdated.length} files have newer versions available:`);
|
|
97
|
+
outdated.forEach((f) => console.log(` ! ${f}`));
|
|
98
|
+
console.log(`\nRun with --force to update: 9000ai init --path ${dest} --force`);
|
|
99
|
+
}
|
|
100
|
+
if (created.length === 0 && updated.length === 0 && outdated.length === 0) {
|
|
101
|
+
console.log("Everything is up to date.");
|
|
75
102
|
return;
|
|
76
103
|
}
|
|
77
104
|
console.log(`\nWorkspace ready: ${dest}`);
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
105
|
+
if (created.length > 0 && updated.length === 0) {
|
|
106
|
+
console.log(`\nNext steps:`);
|
|
107
|
+
console.log(` 1. Fill in profile/ (identity.md, voice.md, topics.md, product.md)`);
|
|
108
|
+
console.log(` 2. cd ${dest}`);
|
|
109
|
+
console.log(` 3. Start working`);
|
|
110
|
+
}
|
|
82
111
|
});
|
|
83
112
|
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, cpSync, readFileSync, readdirSync, statSync } from "fs";
|
|
2
|
+
import { dirname, join, relative } from "path";
|
|
3
|
+
import { homedir } from "os";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
5
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
export function registerUpdateCommand(program) {
|
|
7
|
+
program
|
|
8
|
+
.command("update")
|
|
9
|
+
.description("Sync skills to latest version after npm update")
|
|
10
|
+
.action(() => {
|
|
11
|
+
const src = join(__dirname, "..", "skills");
|
|
12
|
+
const dest = join(homedir(), ".9000ai", "skills");
|
|
13
|
+
if (!existsSync(src)) {
|
|
14
|
+
console.error("Error: Skills not found in package. Reinstall: npm install -g @9000ai/cli");
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
// Diff before copy
|
|
18
|
+
const created = [];
|
|
19
|
+
const updated = [];
|
|
20
|
+
function diffDir(srcDir, destDir) {
|
|
21
|
+
if (!existsSync(srcDir))
|
|
22
|
+
return;
|
|
23
|
+
for (const entry of readdirSync(srcDir)) {
|
|
24
|
+
const s = join(srcDir, entry);
|
|
25
|
+
const d = join(destDir, entry);
|
|
26
|
+
if (statSync(s).isDirectory()) {
|
|
27
|
+
diffDir(s, d);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
const rel = relative(dest, d);
|
|
31
|
+
if (!existsSync(d)) {
|
|
32
|
+
created.push(rel);
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
try {
|
|
36
|
+
if (!readFileSync(s).equals(readFileSync(d))) {
|
|
37
|
+
updated.push(rel);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
updated.push(rel);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
diffDir(src, dest);
|
|
48
|
+
// Copy
|
|
49
|
+
mkdirSync(dest, { recursive: true });
|
|
50
|
+
cpSync(src, dest, { recursive: true, force: true });
|
|
51
|
+
// Read version
|
|
52
|
+
let version = "unknown";
|
|
53
|
+
try {
|
|
54
|
+
const pkg = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf-8"));
|
|
55
|
+
version = pkg.version;
|
|
56
|
+
}
|
|
57
|
+
catch { }
|
|
58
|
+
// Read changelog
|
|
59
|
+
let changelog = "";
|
|
60
|
+
try {
|
|
61
|
+
changelog = readFileSync(join(__dirname, "..", "CHANGELOG.md"), "utf-8");
|
|
62
|
+
}
|
|
63
|
+
catch { }
|
|
64
|
+
// Output
|
|
65
|
+
console.log(`@9000ai/cli v${version}\n`);
|
|
66
|
+
if (created.length > 0 || updated.length > 0) {
|
|
67
|
+
if (created.length > 0) {
|
|
68
|
+
console.log(`New files:`);
|
|
69
|
+
created.forEach((f) => console.log(` + ${f}`));
|
|
70
|
+
}
|
|
71
|
+
if (updated.length > 0) {
|
|
72
|
+
console.log(`Updated files:`);
|
|
73
|
+
updated.forEach((f) => console.log(` ~ ${f}`));
|
|
74
|
+
}
|
|
75
|
+
console.log("");
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
console.log(`Skills: already up to date\n`);
|
|
79
|
+
}
|
|
80
|
+
if (changelog) {
|
|
81
|
+
console.log(changelog);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -9,11 +9,12 @@ import { registerTaskCommands } from "./commands/task.js";
|
|
|
9
9
|
import { registerFeedbackCommands } from "./commands/feedback.js";
|
|
10
10
|
import { registerSkillCommands } from "./commands/skill.js";
|
|
11
11
|
import { registerInitCommand } from "./commands/init.js";
|
|
12
|
+
import { registerUpdateCommand } from "./commands/update.js";
|
|
12
13
|
const program = new Command();
|
|
13
14
|
program
|
|
14
15
|
.name("9000ai")
|
|
15
16
|
.description("9000AI Toolbox CLI — unified interface for 9000AI platform")
|
|
16
|
-
.version("0.5.
|
|
17
|
+
.version("0.5.3");
|
|
17
18
|
registerConfigCommands(program);
|
|
18
19
|
registerAuthCommands(program);
|
|
19
20
|
registerSearchCommands(program);
|
|
@@ -23,6 +24,7 @@ registerTaskCommands(program);
|
|
|
23
24
|
registerFeedbackCommands(program);
|
|
24
25
|
registerSkillCommands(program);
|
|
25
26
|
registerInitCommand(program);
|
|
27
|
+
registerUpdateCommand(program);
|
|
26
28
|
program.parseAsync(process.argv).catch((err) => {
|
|
27
29
|
console.error(err);
|
|
28
30
|
process.exit(1);
|
package/dist/postinstall.d.ts
CHANGED
package/dist/postinstall.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* postinstall: copy bundled skills to ~/.9000ai/skills/ and show
|
|
3
|
+
* postinstall: copy bundled skills to ~/.9000ai/skills/ and show what changed
|
|
4
4
|
*/
|
|
5
|
-
import { existsSync, mkdirSync, cpSync, readFileSync } from "fs";
|
|
6
|
-
import { dirname, join } from "path";
|
|
5
|
+
import { existsSync, mkdirSync, cpSync, readFileSync, readdirSync, statSync } from "fs";
|
|
6
|
+
import { dirname, join, relative } from "path";
|
|
7
7
|
import { homedir } from "os";
|
|
8
8
|
import { fileURLToPath } from "url";
|
|
9
9
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
@@ -19,6 +19,38 @@ catch { }
|
|
|
19
19
|
if (!existsSync(src)) {
|
|
20
20
|
process.exit(0);
|
|
21
21
|
}
|
|
22
|
+
// Diff before copy
|
|
23
|
+
const created = [];
|
|
24
|
+
const updated = [];
|
|
25
|
+
function diffDir(srcDir, destDir) {
|
|
26
|
+
if (!existsSync(srcDir))
|
|
27
|
+
return;
|
|
28
|
+
for (const entry of readdirSync(srcDir)) {
|
|
29
|
+
const s = join(srcDir, entry);
|
|
30
|
+
const d = join(destDir, entry);
|
|
31
|
+
if (statSync(s).isDirectory()) {
|
|
32
|
+
diffDir(s, d);
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
const rel = relative(dest, d);
|
|
36
|
+
if (!existsSync(d)) {
|
|
37
|
+
created.push(rel);
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
try {
|
|
41
|
+
if (!readFileSync(s).equals(readFileSync(d))) {
|
|
42
|
+
updated.push(rel);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
updated.push(rel);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
diffDir(src, dest);
|
|
53
|
+
// Copy
|
|
22
54
|
mkdirSync(dest, { recursive: true });
|
|
23
55
|
cpSync(src, dest, { recursive: true, force: true });
|
|
24
56
|
// Read changelog
|
|
@@ -27,11 +59,24 @@ try {
|
|
|
27
59
|
changelog = readFileSync(join(__dirname, "..", "CHANGELOG.md"), "utf-8");
|
|
28
60
|
}
|
|
29
61
|
catch { }
|
|
62
|
+
// Output
|
|
30
63
|
console.log(`\n@9000ai/cli v${version} installed\n`);
|
|
31
|
-
|
|
64
|
+
if (created.length > 0 || updated.length > 0) {
|
|
65
|
+
if (created.length > 0) {
|
|
66
|
+
console.log(`New files:`);
|
|
67
|
+
created.forEach((f) => console.log(` + ${f}`));
|
|
68
|
+
}
|
|
69
|
+
if (updated.length > 0) {
|
|
70
|
+
console.log(`Updated files:`);
|
|
71
|
+
updated.forEach((f) => console.log(` ~ ${f}`));
|
|
72
|
+
}
|
|
73
|
+
console.log("");
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
console.log(`Skills: up to date\n`);
|
|
77
|
+
}
|
|
32
78
|
if (changelog) {
|
|
33
79
|
console.log(changelog);
|
|
34
|
-
console.log("");
|
|
35
80
|
}
|
|
36
81
|
console.log(`Get started:`);
|
|
37
82
|
console.log(` 1. 9000ai config set --base-url <server-url> --api-key <key>`);
|
package/package.json
CHANGED
|
@@ -115,6 +115,22 @@ output-format: routing-only
|
|
|
115
115
|
|
|
116
116
|
> 画像建好了。现在可以开始——比如"帮我做选题调研"、"看看最近热榜"。
|
|
117
117
|
|
|
118
|
+
## 更新系统
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
npm update -g @9000ai/cli && 9000ai update
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
第一条更新 CLI,第二条同步 skills 并显示更新内容。
|
|
125
|
+
|
|
126
|
+
项目目录的模板也要同步时:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
9000ai init --path <项目目录> --force
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
profile/ 和 claims/ 等用户数据不会被覆盖。
|
|
133
|
+
|
|
118
134
|
## 参考文档
|
|
119
135
|
|
|
120
136
|
需要更详细的使用规范时查阅:
|