glm-coding-router 0.1.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 +21 -0
- package/README.md +248 -0
- package/dist/bin/glm-chat.js +40 -0
- package/dist/bin/glm-review.js +47 -0
- package/dist/bin/glm-worker.js +57 -0
- package/dist/cli.js +108 -0
- package/dist/commands/config.js +39 -0
- package/dist/commands/context.js +20 -0
- package/dist/commands/doctor-command.js +64 -0
- package/dist/commands/doctor.js +93 -0
- package/dist/commands/init.js +123 -0
- package/dist/commands/key.js +56 -0
- package/dist/commands/project-init.js +61 -0
- package/dist/commands/project-remove.js +36 -0
- package/dist/commands/skill.js +43 -0
- package/dist/commands/status.js +57 -0
- package/dist/commands/uninstall.js +81 -0
- package/dist/core/claude.js +104 -0
- package/dist/core/config.js +150 -0
- package/dist/core/env.js +22 -0
- package/dist/core/errors.js +121 -0
- package/dist/core/logging.js +54 -0
- package/dist/core/main-guard.js +20 -0
- package/dist/core/paths.js +13 -0
- package/dist/core/platform.js +20 -0
- package/dist/core/process.js +60 -0
- package/dist/core/prompt.js +32 -0
- package/dist/core/version.js +3 -0
- package/dist/core/zai-key.js +84 -0
- package/dist/integrations/claude.js +18 -0
- package/dist/integrations/codex.js +18 -0
- package/dist/integrations/index.js +3 -0
- package/dist/integrations/skill.js +35 -0
- package/dist/project/atomic-write.js +21 -0
- package/dist/project/managed-block.js +100 -0
- package/dist/project/managed-file.js +67 -0
- package/dist/project/ownership.js +42 -0
- package/dist/project/project-root.js +26 -0
- package/dist/templates/agents-block.js +46 -0
- package/dist/templates/claude-block.js +49 -0
- package/dist/templates/glm-delegation-skill.js +68 -0
- package/package.json +46 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* Atomic file replacement (spec §22): write tmp → fsync → rename.
|
|
5
|
+
* On failure the original file is left untouched. Callers translate
|
|
6
|
+
* filesystem errors into their own actionable errors.
|
|
7
|
+
*/
|
|
8
|
+
export function atomicWriteFile(filePath, content) {
|
|
9
|
+
const dir = path.dirname(filePath);
|
|
10
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
11
|
+
const tmp = `${filePath}.glm-tmp-${process.pid}-${Date.now()}`;
|
|
12
|
+
const fd = fs.openSync(tmp, "w");
|
|
13
|
+
try {
|
|
14
|
+
fs.writeFileSync(fd, content, "utf8");
|
|
15
|
+
fs.fsyncSync(fd);
|
|
16
|
+
}
|
|
17
|
+
finally {
|
|
18
|
+
fs.closeSync(fd);
|
|
19
|
+
}
|
|
20
|
+
fs.renameSync(tmp, filePath);
|
|
21
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { Errors } from "../core/errors.js";
|
|
2
|
+
export const START_MARKER = "<!-- glm-coding-router:start -->";
|
|
3
|
+
export const END_MARKER = "<!-- glm-coding-router:end -->";
|
|
4
|
+
export class ManagedBlockError extends Error {
|
|
5
|
+
constructor(message) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = "ManagedBlockError";
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
/** Detect the file's dominant EOL; default LF (spec §48). */
|
|
11
|
+
function detectEol(content) {
|
|
12
|
+
const firstCr = content.indexOf("\r");
|
|
13
|
+
if (firstCr === -1)
|
|
14
|
+
return "\n";
|
|
15
|
+
const firstLf = content.indexOf("\n");
|
|
16
|
+
if (firstLf === -1)
|
|
17
|
+
return "\n";
|
|
18
|
+
return firstCr < firstLf - 1 ? "\n" : "\r\n";
|
|
19
|
+
}
|
|
20
|
+
function convertEol(text, eol) {
|
|
21
|
+
return eol === "\n" ? text.replace(/\r\n/g, "\n") : text.replace(/\r\n/g, "\n").replace(/\n/g, "\r\n");
|
|
22
|
+
}
|
|
23
|
+
function countOccurrences(content, needle) {
|
|
24
|
+
return content.split(needle).length - 1;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Validate marker pairing (spec §48): on a malformed pair the caller must
|
|
28
|
+
* not modify the file and must surface an actionable error.
|
|
29
|
+
*/
|
|
30
|
+
export function validateMarkers(content, fileName) {
|
|
31
|
+
const starts = countOccurrences(content, START_MARKER);
|
|
32
|
+
const ends = countOccurrences(content, END_MARKER);
|
|
33
|
+
if (starts === 0 && ends === 0)
|
|
34
|
+
return;
|
|
35
|
+
if (starts > 1) {
|
|
36
|
+
throw Errors.managedBlockCorrupt(fileName, "multiple start markers");
|
|
37
|
+
}
|
|
38
|
+
if (ends > 1) {
|
|
39
|
+
throw Errors.managedBlockCorrupt(fileName, "multiple end markers");
|
|
40
|
+
}
|
|
41
|
+
if (starts === 1 && ends === 0) {
|
|
42
|
+
throw Errors.managedBlockCorrupt(fileName, "start marker without end marker");
|
|
43
|
+
}
|
|
44
|
+
if (starts === 0 && ends === 1) {
|
|
45
|
+
throw Errors.managedBlockCorrupt(fileName, "end marker without start marker");
|
|
46
|
+
}
|
|
47
|
+
if (content.indexOf(START_MARKER) > content.indexOf(END_MARKER)) {
|
|
48
|
+
throw Errors.managedBlockCorrupt(fileName, "end marker appears before start marker");
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
export function hasManagedBlock(content) {
|
|
52
|
+
return content.includes(START_MARKER) || content.includes(END_MARKER);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Insert or replace the managed block (spec §21):
|
|
56
|
+
* - absent → append with a blank-line separator
|
|
57
|
+
* - present → replace in place, preserving everything outside the markers
|
|
58
|
+
* Idempotent: never duplicates, preserves the file's EOL style and
|
|
59
|
+
* trailing-newline state.
|
|
60
|
+
*/
|
|
61
|
+
export function upsertManagedBlock(content, block, fileName) {
|
|
62
|
+
validateMarkers(content, fileName);
|
|
63
|
+
const eol = detectEol(content);
|
|
64
|
+
const blockText = convertEol(block.trim(), eol);
|
|
65
|
+
if (content.includes(START_MARKER)) {
|
|
66
|
+
const startIdx = content.indexOf(START_MARKER);
|
|
67
|
+
const endIdx = content.indexOf(END_MARKER) + END_MARKER.length;
|
|
68
|
+
const prefix = content.slice(0, startIdx);
|
|
69
|
+
const suffix = content.slice(endIdx);
|
|
70
|
+
return prefix + blockText + suffix;
|
|
71
|
+
}
|
|
72
|
+
if (content.trim().length === 0) {
|
|
73
|
+
return blockText + eol;
|
|
74
|
+
}
|
|
75
|
+
const base = content.replace(/(?:\r?\n)+$/, "");
|
|
76
|
+
return base + eol + eol + blockText + eol;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Remove the managed block, preserving all user content (spec §43).
|
|
80
|
+
* A file that contained only the block becomes empty (""), letting callers
|
|
81
|
+
* decide whether to delete a router-created file.
|
|
82
|
+
*/
|
|
83
|
+
export function removeManagedBlock(content, fileName) {
|
|
84
|
+
validateMarkers(content, fileName);
|
|
85
|
+
if (!content.includes(START_MARKER)) {
|
|
86
|
+
return content;
|
|
87
|
+
}
|
|
88
|
+
const eol = detectEol(content);
|
|
89
|
+
const startIdx = content.indexOf(START_MARKER);
|
|
90
|
+
const endIdx = content.indexOf(END_MARKER) + END_MARKER.length;
|
|
91
|
+
const prefix = content.slice(0, startIdx).replace(/(?:\r?\n)+$/, "");
|
|
92
|
+
const suffix = content.slice(endIdx).replace(/^(?:\r?\n)+/, "");
|
|
93
|
+
if (prefix.length === 0 && suffix.length === 0)
|
|
94
|
+
return "";
|
|
95
|
+
if (prefix.length === 0)
|
|
96
|
+
return suffix;
|
|
97
|
+
if (suffix.length === 0)
|
|
98
|
+
return prefix + eol;
|
|
99
|
+
return prefix + eol + eol + suffix;
|
|
100
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { Errors } from "../core/errors.js";
|
|
4
|
+
import { hasManagedBlock, removeManagedBlock, upsertManagedBlock } from "./managed-block.js";
|
|
5
|
+
import { atomicWriteFile } from "./atomic-write.js";
|
|
6
|
+
import { forgetFile, isOwnedFile, recordCreatedFile } from "./ownership.js";
|
|
7
|
+
function toChange(file, created, oldContent, newContent, deleted = false) {
|
|
8
|
+
return {
|
|
9
|
+
file,
|
|
10
|
+
created,
|
|
11
|
+
changed: oldContent !== newContent || deleted,
|
|
12
|
+
oldContent,
|
|
13
|
+
newContent,
|
|
14
|
+
deleted: deleted,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/** Insert or replace the managed block in a file, atomically (spec §21, §22). */
|
|
18
|
+
export function upsertManagedFile(options) {
|
|
19
|
+
const { file, block, home, dryRun } = options;
|
|
20
|
+
const fileName = path.basename(file);
|
|
21
|
+
const existed = fs.existsSync(file);
|
|
22
|
+
const oldContent = existed ? fs.readFileSync(file, "utf8") : "";
|
|
23
|
+
const newContent = upsertManagedBlock(oldContent, block, fileName);
|
|
24
|
+
if (!dryRun && newContent !== oldContent) {
|
|
25
|
+
try {
|
|
26
|
+
atomicWriteFile(file, newContent);
|
|
27
|
+
}
|
|
28
|
+
catch (cause) {
|
|
29
|
+
throw Errors.managedFileWriteFailed(file, cause instanceof Error ? cause.message : String(cause));
|
|
30
|
+
}
|
|
31
|
+
if (!existed) {
|
|
32
|
+
recordCreatedFile(file, home);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return toChange(file, !existed, oldContent, newContent);
|
|
36
|
+
}
|
|
37
|
+
/** Remove the managed block; delete router-created files that become empty (spec §43). */
|
|
38
|
+
export function removeManagedFile(file, home, dryRun = false) {
|
|
39
|
+
const fileName = path.basename(file);
|
|
40
|
+
if (!fs.existsSync(file)) {
|
|
41
|
+
return toChange(file, false, "", "", false);
|
|
42
|
+
}
|
|
43
|
+
const oldContent = fs.readFileSync(file, "utf8");
|
|
44
|
+
if (!hasManagedBlock(oldContent)) {
|
|
45
|
+
return toChange(file, false, oldContent, oldContent, false);
|
|
46
|
+
}
|
|
47
|
+
const newContent = removeManagedBlock(oldContent, fileName);
|
|
48
|
+
const owned = isOwnedFile(file, home);
|
|
49
|
+
const emptyAfterRemove = newContent.length === 0;
|
|
50
|
+
if (dryRun) {
|
|
51
|
+
return toChange(file, false, oldContent, newContent, owned && emptyAfterRemove);
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
if (emptyAfterRemove && owned) {
|
|
55
|
+
fs.rmSync(file);
|
|
56
|
+
forgetFile(file, home);
|
|
57
|
+
return toChange(file, false, oldContent, newContent, true);
|
|
58
|
+
}
|
|
59
|
+
if (newContent !== oldContent) {
|
|
60
|
+
atomicWriteFile(file, newContent);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch (cause) {
|
|
64
|
+
throw Errors.managedFileWriteFailed(file, cause instanceof Error ? cause.message : String(cause));
|
|
65
|
+
}
|
|
66
|
+
return toChange(file, false, oldContent, newContent, false);
|
|
67
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { ownershipPath } from "../core/paths.js";
|
|
4
|
+
function readRecords(home) {
|
|
5
|
+
const file = ownershipPath(home);
|
|
6
|
+
try {
|
|
7
|
+
const raw = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
8
|
+
if (raw && typeof raw.files === "object" && raw.files !== null) {
|
|
9
|
+
return { files: raw.files };
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
// Missing or corrupt metadata is non-fatal: fall back to empty.
|
|
14
|
+
}
|
|
15
|
+
return { files: {} };
|
|
16
|
+
}
|
|
17
|
+
function writeRecords(record, home) {
|
|
18
|
+
const file = ownershipPath(home);
|
|
19
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
20
|
+
const tmp = `${file}.tmp-${process.pid}`;
|
|
21
|
+
fs.writeFileSync(tmp, JSON.stringify(record, null, 2) + "\n", "utf8");
|
|
22
|
+
fs.renameSync(tmp, file);
|
|
23
|
+
}
|
|
24
|
+
export function recordCreatedFile(filePath, home) {
|
|
25
|
+
const normalized = path.resolve(filePath);
|
|
26
|
+
const records = readRecords(home);
|
|
27
|
+
if (normalized in records.files)
|
|
28
|
+
return;
|
|
29
|
+
records.files[normalized] = { createdAt: new Date().toISOString() };
|
|
30
|
+
writeRecords(records, home);
|
|
31
|
+
}
|
|
32
|
+
export function isOwnedFile(filePath, home) {
|
|
33
|
+
return path.resolve(filePath) in readRecords(home).files;
|
|
34
|
+
}
|
|
35
|
+
export function forgetFile(filePath, home) {
|
|
36
|
+
const normalized = path.resolve(filePath);
|
|
37
|
+
const records = readRecords(home);
|
|
38
|
+
if (!(normalized in records.files))
|
|
39
|
+
return;
|
|
40
|
+
delete records.files[normalized];
|
|
41
|
+
writeRecords(records, home);
|
|
42
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { logger } from "../core/logging.js";
|
|
4
|
+
/**
|
|
5
|
+
* Project root detection (spec §19):
|
|
6
|
+
* git rev-parse --show-toplevel → cwd
|
|
7
|
+
*/
|
|
8
|
+
export function findProjectRoot(cwd = process.cwd()) {
|
|
9
|
+
try {
|
|
10
|
+
const output = execFileSync("git", ["rev-parse", "--show-toplevel"], {
|
|
11
|
+
cwd,
|
|
12
|
+
encoding: "utf8",
|
|
13
|
+
windowsHide: true,
|
|
14
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
15
|
+
});
|
|
16
|
+
const root = output.trim();
|
|
17
|
+
if (root.length > 0) {
|
|
18
|
+
// git prints forward slashes on Windows; normalize for the platform.
|
|
19
|
+
return path.resolve(root);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
logger.debug(`git rev-parse failed (${error instanceof Error ? error.message : "unknown"}); using cwd`);
|
|
24
|
+
}
|
|
25
|
+
return cwd;
|
|
26
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/** Managed block content for AGENTS.md (spec §24). Keep in sync with the spec. */
|
|
2
|
+
export const AGENTS_MANAGED_BLOCK = `<!-- glm-coding-router:start -->
|
|
3
|
+
|
|
4
|
+
## GLM Worker Delegation
|
|
5
|
+
|
|
6
|
+
Available commands:
|
|
7
|
+
|
|
8
|
+
- \`glm-worker "<task>"\`
|
|
9
|
+
- \`glm-review "<task>"\`
|
|
10
|
+
|
|
11
|
+
Codex is the primary orchestrator.
|
|
12
|
+
|
|
13
|
+
Delegate:
|
|
14
|
+
- CRUD
|
|
15
|
+
- boilerplate
|
|
16
|
+
- tests
|
|
17
|
+
- documentation
|
|
18
|
+
- mechanical refactoring
|
|
19
|
+
- repository exploration
|
|
20
|
+
- straightforward implementation
|
|
21
|
+
|
|
22
|
+
Keep in Codex:
|
|
23
|
+
- requirements
|
|
24
|
+
- planning
|
|
25
|
+
- architecture
|
|
26
|
+
- ambiguous business logic
|
|
27
|
+
- complex debugging
|
|
28
|
+
- security decisions
|
|
29
|
+
- integration
|
|
30
|
+
- final review
|
|
31
|
+
|
|
32
|
+
Before delegation create a task packet:
|
|
33
|
+
|
|
34
|
+
TASK
|
|
35
|
+
SCOPE
|
|
36
|
+
FILES ALLOWED TO MODIFY
|
|
37
|
+
FILES NOT TO MODIFY
|
|
38
|
+
REQUIREMENTS
|
|
39
|
+
CONSTRAINTS
|
|
40
|
+
ACCEPTANCE CRITERIA
|
|
41
|
+
VALIDATION
|
|
42
|
+
EXPECTED OUTPUT
|
|
43
|
+
|
|
44
|
+
Never trust a worker's success report without inspecting the resulting changes.
|
|
45
|
+
|
|
46
|
+
<!-- glm-coding-router:end -->`;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/** Managed block content for CLAUDE.md (spec §20). Keep in sync with the spec. */
|
|
2
|
+
export const CLAUDE_MANAGED_BLOCK = `<!-- glm-coding-router:start -->
|
|
3
|
+
|
|
4
|
+
## GLM Worker Delegation
|
|
5
|
+
|
|
6
|
+
GLM workers available:
|
|
7
|
+
|
|
8
|
+
- \`glm-worker "<task>"\`
|
|
9
|
+
- \`glm-review "<task>"\`
|
|
10
|
+
|
|
11
|
+
Delegate well-scoped, implementation-heavy work to GLM.
|
|
12
|
+
|
|
13
|
+
Use GLM for:
|
|
14
|
+
- repository exploration
|
|
15
|
+
- CRUD
|
|
16
|
+
- boilerplate
|
|
17
|
+
- tests
|
|
18
|
+
- documentation
|
|
19
|
+
- mechanical refactoring
|
|
20
|
+
- straightforward implementation
|
|
21
|
+
|
|
22
|
+
Claude remains responsible for:
|
|
23
|
+
- requirements
|
|
24
|
+
- architecture
|
|
25
|
+
- ambiguous business rules
|
|
26
|
+
- security-sensitive decisions
|
|
27
|
+
- complex debugging
|
|
28
|
+
- integration
|
|
29
|
+
- final review
|
|
30
|
+
|
|
31
|
+
Before delegation, define:
|
|
32
|
+
- task
|
|
33
|
+
- scope
|
|
34
|
+
- allowed files
|
|
35
|
+
- forbidden files
|
|
36
|
+
- requirements
|
|
37
|
+
- constraints
|
|
38
|
+
- acceptance criteria
|
|
39
|
+
- validation command
|
|
40
|
+
- expected output
|
|
41
|
+
|
|
42
|
+
After worker completion:
|
|
43
|
+
1. inspect the actual diff
|
|
44
|
+
2. validate against requirements
|
|
45
|
+
3. run relevant tests
|
|
46
|
+
4. resolve integration problems
|
|
47
|
+
5. accept only after verification
|
|
48
|
+
|
|
49
|
+
<!-- glm-coding-router:end -->`;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/** Codex skill definition (spec §26). Keep in sync with the spec. */
|
|
2
|
+
export const GLM_DELEGATION_SKILL_NAME = "glm-delegation";
|
|
3
|
+
export const GLM_DELEGATION_SKILL_MD = `---
|
|
4
|
+
name: glm-delegation
|
|
5
|
+
description: >
|
|
6
|
+
Delegate well-scoped implementation, testing,
|
|
7
|
+
repository exploration, boilerplate, CRUD,
|
|
8
|
+
documentation, and mechanical refactoring to
|
|
9
|
+
GLM Coding Plan workers.
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# GLM Delegation
|
|
13
|
+
|
|
14
|
+
Available commands:
|
|
15
|
+
|
|
16
|
+
glm-worker "<task>"
|
|
17
|
+
glm-review "<task>"
|
|
18
|
+
|
|
19
|
+
## Use glm-review for
|
|
20
|
+
|
|
21
|
+
- repository exploration
|
|
22
|
+
- dependency analysis
|
|
23
|
+
- locating implementations
|
|
24
|
+
- call-chain discovery
|
|
25
|
+
- code review
|
|
26
|
+
|
|
27
|
+
## Use glm-worker for
|
|
28
|
+
|
|
29
|
+
- CRUD
|
|
30
|
+
- unit tests
|
|
31
|
+
- implementation
|
|
32
|
+
- documentation
|
|
33
|
+
- repetitive changes
|
|
34
|
+
- mechanical refactoring
|
|
35
|
+
|
|
36
|
+
## Keep in primary Codex agent
|
|
37
|
+
|
|
38
|
+
- requirements
|
|
39
|
+
- architecture
|
|
40
|
+
- ambiguous rules
|
|
41
|
+
- security-sensitive design
|
|
42
|
+
- difficult debugging
|
|
43
|
+
- integration
|
|
44
|
+
- final acceptance
|
|
45
|
+
|
|
46
|
+
## Delegation packet
|
|
47
|
+
|
|
48
|
+
Always provide:
|
|
49
|
+
|
|
50
|
+
TASK
|
|
51
|
+
SCOPE
|
|
52
|
+
ALLOWED FILES
|
|
53
|
+
FORBIDDEN FILES
|
|
54
|
+
REQUIREMENTS
|
|
55
|
+
CONSTRAINTS
|
|
56
|
+
ACCEPTANCE CRITERIA
|
|
57
|
+
VALIDATION
|
|
58
|
+
EXPECTED OUTPUT
|
|
59
|
+
|
|
60
|
+
## Verification
|
|
61
|
+
|
|
62
|
+
After GLM finishes:
|
|
63
|
+
|
|
64
|
+
- inspect the actual diff
|
|
65
|
+
- independently run relevant validation
|
|
66
|
+
- compare implementation with requirements
|
|
67
|
+
- reject or correct worker output when needed
|
|
68
|
+
`;
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "glm-coding-router",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "GLM Coding Plan workers for Claude Code and Codex",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "hieu9721",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/hieu9721/GLM-coding-router.git"
|
|
11
|
+
},
|
|
12
|
+
"bin": {
|
|
13
|
+
"glm-router": "./dist/cli.js",
|
|
14
|
+
"glm-chat": "./dist/bin/glm-chat.js",
|
|
15
|
+
"glm-worker": "./dist/bin/glm-worker.js",
|
|
16
|
+
"glm-review": "./dist/bin/glm-review.js"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"dev": "tsx src/cli.ts",
|
|
23
|
+
"build": "tsc",
|
|
24
|
+
"test": "vitest run",
|
|
25
|
+
"test:watch": "vitest",
|
|
26
|
+
"lint": "eslint src tests",
|
|
27
|
+
"prepublishOnly": "npm run build && npm test"
|
|
28
|
+
},
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=20"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"commander": "^15.0.0",
|
|
34
|
+
"prompts": "^2.4.2",
|
|
35
|
+
"zod": "^4.6.5"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@types/node": "^22.20.3",
|
|
39
|
+
"@types/prompts": "^2.4.9",
|
|
40
|
+
"eslint": "^9.39.5",
|
|
41
|
+
"tsx": "^4.23.13",
|
|
42
|
+
"typescript": "^5.9.3",
|
|
43
|
+
"typescript-eslint": "^8.70.0",
|
|
44
|
+
"vitest": "^5.0.1"
|
|
45
|
+
}
|
|
46
|
+
}
|