@aegean-org/necode-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/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Mario Zechner
4
+ Copyright (c) 2025-2026 Can Bölük
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,115 @@
1
+ # @aegean-org/necode-cli
2
+
3
+ NE-focused coding agent CLI.
4
+
5
+ [中文](#中文) | [English](#english)
6
+
7
+ ## 中文
8
+
9
+ ### 安装
10
+
11
+ ```sh
12
+ npm install -g @aegean-org/necode-cli
13
+ necode
14
+ ```
15
+
16
+ 包发布在 npm registry,包名是 `@aegean-org/necode-cli`,安装后的命令是 `necode`。这是一个集成 CLI 包,安装时会从公开的 `liangwei/necode-cli-releases` GitHub Releases 下载当前平台二进制;普通用户不需要安装 Bun,也不需要单独安装内部 `pi-*` 包。
17
+
18
+ ### 登录
19
+
20
+ 在 TUI 中执行 `/login` 登录 NE。正常对话前必须完成 NE 登录。
21
+
22
+ ### 本地文献 RAG
23
+
24
+ 索引本地文献:
25
+
26
+ ```text
27
+ /rag-import <file-or-directory>
28
+ ```
29
+
30
+ 在输入中引用已索引文献:
31
+
32
+ ```text
33
+ @文献
34
+ @文献:keyword
35
+ @doc
36
+ @doc:keyword
37
+ ```
38
+
39
+ 选择文献后会插入可读标题,例如 `@文献:"Paper Title"` 或 `@doc:"Paper Title"`。
40
+
41
+ ### 默认 MCP 服务
42
+
43
+ necode 默认引入这些外部 MCP 服务:
44
+
45
+ - `noteexpress`: NE / NoteExpress MCP 服务
46
+ - `qingtibase`: 青提 MCP 服务
47
+
48
+ 在 TUI 中使用 `/mcp list` 查看连接状态和可用工具。
49
+
50
+ ### 发布
51
+
52
+ 只发布 `@aegean-org/necode-cli` 一个 npm 包。内部 workspace 包会编译进平台二进制,并作为 `liangwei/necode-cli-releases` 的 GitHub Release assets 分发,不单独发布到 npm。
53
+
54
+ ```sh
55
+ npm run build:necode:binaries -- --targets win32-x64
56
+ npm run pack:necode -- --targets win32-x64
57
+ npm run publish:necode -- --targets win32-x64
58
+ ```
59
+
60
+ npm 包会保持小体积,并包含二进制校验清单。跨平台包需要先把 `darwin-arm64,darwin-x64,linux-arm64,linux-x64,win32-x64` 的二进制都放到 `packages/coding-agent/binaries/`,发布时还要先把这些文件上传到 `liangwei/necode-cli-releases` 的匹配版本 GitHub Release。
61
+
62
+ ## English
63
+
64
+ ### Install
65
+
66
+ ```sh
67
+ npm install -g @aegean-org/necode-cli
68
+ necode
69
+ ```
70
+
71
+ The package is published to the npm registry as `@aegean-org/necode-cli`, and the installed command is `necode`. It is an integrated CLI package; install downloads the matching platform binary from the public `liangwei/necode-cli-releases` GitHub Releases, so normal users do not need Bun or separate internal `pi-*` packages.
72
+
73
+ ### Login
74
+
75
+ Run `/login` in the TUI to sign in with NE. A valid NE login is required before normal chat usage.
76
+
77
+ ### Local Literature RAG
78
+
79
+ Index local papers:
80
+
81
+ ```text
82
+ /rag-import <file-or-directory>
83
+ ```
84
+
85
+ Use indexed literature explicitly in a prompt:
86
+
87
+ ```text
88
+ @文献
89
+ @文献:keyword
90
+ @doc
91
+ @doc:keyword
92
+ ```
93
+
94
+ Selecting an indexed document inserts a readable title such as `@文献:"Paper Title"` or `@doc:"Paper Title"`.
95
+
96
+ ### Default MCP Servers
97
+
98
+ necode includes these default external MCP servers:
99
+
100
+ - `noteexpress`: NE / NoteExpress MCP server
101
+ - `qingtibase`: Qingti MCP server
102
+
103
+ Use `/mcp list` inside the TUI to check whether they are connected and which tools are available.
104
+
105
+ ### Publishing
106
+
107
+ Only `@aegean-org/necode-cli` is published to npm. Internal workspace packages are compiled into platform binaries distributed as `liangwei/necode-cli-releases` GitHub Release assets instead of being published separately.
108
+
109
+ ```sh
110
+ npm run build:necode:binaries -- --targets win32-x64
111
+ npm run pack:necode -- --targets win32-x64
112
+ npm run publish:necode -- --targets win32-x64
113
+ ```
114
+
115
+ The npm package stays small and contains a checked binary manifest. A cross-platform package requires binaries for `darwin-arm64,darwin-x64,linux-arm64,linux-x64,win32-x64` to exist in `packages/coding-agent/binaries/` first, and publish runs must upload those files to the matching release in `liangwei/necode-cli-releases` before `npm publish`.
package/bin/necode.js ADDED
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env node
2
+ import { spawnSync } from "node:child_process";
3
+ import * as fs from "node:fs";
4
+ import * as path from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+
7
+ const FAILURE_EXIT_CODE = 1;
8
+ const SUCCESS_EXIT_CODE = 0;
9
+ const USER_ARGV_OFFSET = 2;
10
+ const BINARIES = Object.freeze({
11
+ "darwin-arm64": "necode-cli-darwin-arm64",
12
+ "darwin-x64": "necode-cli-darwin-x64",
13
+ "linux-arm64": "necode-cli-linux-arm64",
14
+ "linux-x64": "necode-cli-linux-x64",
15
+ "win32-x64": "necode-cli-windows-x64.exe",
16
+ });
17
+
18
+ function resolveBinary() {
19
+ const key = `${process.platform}-${process.arch}`;
20
+ const binaryName = BINARIES[key];
21
+ if (!binaryName) {
22
+ throw new Error(`Unsupported platform: ${key}`);
23
+ }
24
+ const packageRoot = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
25
+ const binaryPath = path.join(packageRoot, "binaries", binaryName);
26
+ if (!fs.existsSync(binaryPath)) {
27
+ throw new Error(`Installed necode binary is missing: ${binaryPath}. Reinstall without --ignore-scripts.`);
28
+ }
29
+ return binaryPath;
30
+ }
31
+
32
+ function run() {
33
+ const binaryPath = resolveBinary();
34
+ const result = spawnSync(binaryPath, process.argv.slice(USER_ARGV_OFFSET), {
35
+ stdio: "inherit",
36
+ windowsHide: false,
37
+ });
38
+ if (result.error) {
39
+ throw result.error;
40
+ }
41
+ if (result.signal) {
42
+ process.stderr.write(`necode terminated by signal ${result.signal}\n`);
43
+ process.exit(FAILURE_EXIT_CODE);
44
+ }
45
+ process.exit(result.status ?? SUCCESS_EXIT_CODE);
46
+ }
47
+
48
+ try {
49
+ run();
50
+ } catch (error) {
51
+ const message = error instanceof Error ? error.message : String(error);
52
+ process.stderr.write(`error: ${message}\n`);
53
+ process.exit(FAILURE_EXIT_CODE);
54
+ }
@@ -0,0 +1,31 @@
1
+ {
2
+ "version": "1.0.0",
3
+ "repository": "liangwei/necode-cli-releases",
4
+ "assets": {
5
+ "darwin-arm64": {
6
+ "filename": "necode-cli-darwin-arm64",
7
+ "sha256": "210cfe0ca226da82e3fab887b93ff7173af88ca7e7310b509340ee4f1c61592c",
8
+ "size": 201537632
9
+ },
10
+ "darwin-x64": {
11
+ "filename": "necode-cli-darwin-x64",
12
+ "sha256": "dbad795f958ab25880adffa69a2ef40aa10d4012b776a75374a5c6e963c14c9c",
13
+ "size": 206981312
14
+ },
15
+ "linux-arm64": {
16
+ "filename": "necode-cli-linux-arm64",
17
+ "sha256": "264eb62fd60083e7a7f1bbf5834acd90eab2728712c6629a9ab38404105df0ab",
18
+ "size": 227387536
19
+ },
20
+ "linux-x64": {
21
+ "filename": "necode-cli-linux-x64",
22
+ "sha256": "aaa36b9f039077c39d771af3906df68cdef7f01ee06675861e778133ab4cb944",
23
+ "size": 243677312
24
+ },
25
+ "win32-x64": {
26
+ "filename": "necode-cli-windows-x64.exe",
27
+ "sha256": "4f09f4336ff11d019395d21a595540eb964dd0fb6f05e85539ab389169ba86c6",
28
+ "size": 231872000
29
+ }
30
+ }
31
+ }
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@aegean-org/necode-cli",
3
+ "version": "1.0.0",
4
+ "description": "necode coding agent with read, bash, edit, write tools and session management",
5
+ "author": "Can Boluk",
6
+ "contributors": [
7
+ "Mario Zechner"
8
+ ],
9
+ "license": "MIT",
10
+ "keywords": [
11
+ "coding-agent",
12
+ "ai",
13
+ "llm",
14
+ "cli",
15
+ "tui",
16
+ "agent"
17
+ ],
18
+ "type": "module",
19
+ "bin": {
20
+ "necode": "bin/necode.js"
21
+ },
22
+ "files": [
23
+ "bin",
24
+ "scripts",
25
+ "binary-manifest.json",
26
+ "README.md",
27
+ "CHANGELOG.md",
28
+ "LICENSE"
29
+ ],
30
+ "engines": {
31
+ "node": ">=18"
32
+ },
33
+ "scripts": {
34
+ "postinstall": "node scripts/install-integrated-binary.js"
35
+ },
36
+ "os": [
37
+ "darwin",
38
+ "linux",
39
+ "win32"
40
+ ],
41
+ "cpu": [
42
+ "arm64",
43
+ "x64"
44
+ ]
45
+ }
@@ -0,0 +1,82 @@
1
+ #!/usr/bin/env node
2
+ import { createHash } from "node:crypto";
3
+ import * as fs from "node:fs";
4
+ import * as fsp from "node:fs/promises";
5
+ import * as path from "node:path";
6
+ import { Readable } from "node:stream";
7
+ import { pipeline } from "node:stream/promises";
8
+ import { fileURLToPath } from "node:url";
9
+
10
+ const EXECUTABLE_MODE = 0o755;
11
+ const FAILURE_EXIT_CODE = 1;
12
+ const packageRoot = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
13
+ const manifestPath = path.join(packageRoot, "binary-manifest.json");
14
+ const binariesDir = path.join(packageRoot, "binaries");
15
+
16
+ async function readManifest() {
17
+ const manifest = JSON.parse(await fsp.readFile(manifestPath, "utf8"));
18
+ if (typeof manifest.version !== "string") throw new Error("binary-manifest.json is missing version");
19
+ if (typeof manifest.repository !== "string") throw new Error("binary-manifest.json is missing repository");
20
+ if (!manifest.assets || typeof manifest.assets !== "object") throw new Error("binary-manifest.json is missing assets");
21
+ return manifest;
22
+ }
23
+
24
+ function resolveAsset(manifest) {
25
+ const platformKey = `${process.platform}-${process.arch}`;
26
+ const asset = manifest.assets[platformKey];
27
+ if (!asset) {
28
+ const supported = Object.keys(manifest.assets).sort().join(", ");
29
+ throw new Error(`Unsupported platform ${platformKey}. Supported platforms: ${supported}`);
30
+ }
31
+ if (typeof asset.filename !== "string") throw new Error(`Asset ${platformKey} is missing filename`);
32
+ if (typeof asset.sha256 !== "string") throw new Error(`Asset ${platformKey} is missing sha256`);
33
+ return asset;
34
+ }
35
+
36
+ function releaseAssetUrl(manifest, asset) {
37
+ const tag = `v${manifest.version}`;
38
+ return `https://github.com/${manifest.repository}/releases/download/${tag}/${asset.filename}`;
39
+ }
40
+
41
+ async function sha256File(filePath) {
42
+ const hash = createHash("sha256");
43
+ for await (const chunk of fs.createReadStream(filePath)) {
44
+ hash.update(chunk);
45
+ }
46
+ return hash.digest("hex");
47
+ }
48
+
49
+ async function downloadFile(url, outputPath) {
50
+ const response = await fetch(url);
51
+ if (!response.ok) throw new Error(`Failed to download ${url}: ${response.status} ${response.statusText}`);
52
+ if (!response.body) throw new Error(`Failed to download ${url}: response body is empty`);
53
+ await pipeline(Readable.fromWeb(response.body), fs.createWriteStream(outputPath, { mode: EXECUTABLE_MODE }));
54
+ }
55
+
56
+ async function installBinary() {
57
+ const manifest = await readManifest();
58
+ const asset = resolveAsset(manifest);
59
+ const finalPath = path.join(binariesDir, asset.filename);
60
+ const tmpPath = `${finalPath}.tmp-${process.pid}`;
61
+ await fsp.mkdir(binariesDir, { recursive: true });
62
+ await fsp.rm(tmpPath, { force: true });
63
+ try {
64
+ await downloadFile(releaseAssetUrl(manifest, asset), tmpPath);
65
+ const actualSha256 = await sha256File(tmpPath);
66
+ if (actualSha256 !== asset.sha256) {
67
+ throw new Error(`Checksum mismatch for ${asset.filename}: expected ${asset.sha256}, got ${actualSha256}`);
68
+ }
69
+ await fsp.rename(tmpPath, finalPath);
70
+ if (process.platform !== "win32") await fsp.chmod(finalPath, EXECUTABLE_MODE);
71
+ } finally {
72
+ await fsp.rm(tmpPath, { force: true });
73
+ }
74
+ }
75
+
76
+ try {
77
+ await installBinary();
78
+ } catch (error) {
79
+ const message = error instanceof Error ? error.message : String(error);
80
+ process.stderr.write(`error: ${message}\n`);
81
+ process.exit(FAILURE_EXIT_CODE);
82
+ }