@byterover/claude-plugin 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 +44 -0
- package/README.md +304 -0
- package/dist/bridge-command.d.ts +23 -0
- package/dist/bridge-command.js +103 -0
- package/dist/cc-frontmatter.d.ts +21 -0
- package/dist/cc-frontmatter.js +51 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +20 -0
- package/dist/commands/doctor.d.ts +2 -0
- package/dist/commands/doctor.js +150 -0
- package/dist/commands/ingest.d.ts +2 -0
- package/dist/commands/ingest.js +101 -0
- package/dist/commands/install.d.ts +2 -0
- package/dist/commands/install.js +130 -0
- package/dist/commands/recall.d.ts +2 -0
- package/dist/commands/recall.js +41 -0
- package/dist/commands/sync.d.ts +2 -0
- package/dist/commands/sync.js +122 -0
- package/dist/commands/uninstall.d.ts +2 -0
- package/dist/commands/uninstall.js +72 -0
- package/dist/memory-path.d.ts +16 -0
- package/dist/memory-path.js +214 -0
- package/dist/schemas/cc-hook-input.d.ts +129 -0
- package/dist/schemas/cc-hook-input.js +30 -0
- package/dist/schemas/cc-settings.d.ts +24 -0
- package/dist/schemas/cc-settings.js +35 -0
- package/dist/stdin.d.ts +7 -0
- package/dist/stdin.js +24 -0
- package/package.json +62 -0
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { readFileSync, realpathSync, statSync, } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { basename, dirname, isAbsolute, join, normalize, resolve, sep, } from "node:path";
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// djb2 hash — exact match of cc-ts/utils/hash.ts
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
function djb2Hash(str) {
|
|
8
|
+
let hash = 0;
|
|
9
|
+
for (let i = 0; i < str.length; i++) {
|
|
10
|
+
hash = ((hash << 5) - hash + str.charCodeAt(i)) | 0;
|
|
11
|
+
}
|
|
12
|
+
return hash;
|
|
13
|
+
}
|
|
14
|
+
function simpleHash(str) {
|
|
15
|
+
return Math.abs(djb2Hash(str)).toString(36);
|
|
16
|
+
}
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
// Path sanitization — exact match of cc-ts/utils/sessionStoragePortable.ts
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
const MAX_SANITIZED_LENGTH = 200;
|
|
21
|
+
export function sanitizePath(name) {
|
|
22
|
+
const sanitized = name.replace(/[^a-zA-Z0-9]/g, "-");
|
|
23
|
+
if (sanitized.length <= MAX_SANITIZED_LENGTH) {
|
|
24
|
+
return sanitized;
|
|
25
|
+
}
|
|
26
|
+
return `${sanitized.slice(0, MAX_SANITIZED_LENGTH)}-${simpleHash(name)}`;
|
|
27
|
+
}
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
// Claude config home
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
export function getClaudeConfigHome() {
|
|
32
|
+
return (process.env.CLAUDE_CONFIG_DIR ?? join(homedir(), ".claude")).normalize("NFC");
|
|
33
|
+
}
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
// Memory base directory
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
function getMemoryBaseDir() {
|
|
38
|
+
return process.env.CLAUDE_CODE_REMOTE_MEMORY_DIR ?? getClaudeConfigHome();
|
|
39
|
+
}
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
// Memory path validation — matches cc-ts/memdir/paths.ts validateMemoryPath
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
function validateMemoryPath(raw, expandTilde) {
|
|
44
|
+
if (!raw || typeof raw !== "string")
|
|
45
|
+
return undefined;
|
|
46
|
+
let candidate = raw;
|
|
47
|
+
// Expand ~/ if allowed (settings: yes, env var: no)
|
|
48
|
+
if (expandTilde &&
|
|
49
|
+
(candidate.startsWith("~/") || candidate.startsWith("~\\"))) {
|
|
50
|
+
candidate = join(homedir(), candidate.slice(2));
|
|
51
|
+
}
|
|
52
|
+
// Normalize and strip trailing separators
|
|
53
|
+
const normalized = normalize(candidate).replace(/[/\\]+$/, "");
|
|
54
|
+
// Reject non-absolute, too short, or dangerous paths
|
|
55
|
+
if (!isAbsolute(normalized))
|
|
56
|
+
return undefined;
|
|
57
|
+
if (normalized.length < 3)
|
|
58
|
+
return undefined;
|
|
59
|
+
if (normalized.includes("\0"))
|
|
60
|
+
return undefined;
|
|
61
|
+
// Reject UNC paths on Windows
|
|
62
|
+
if (normalized.startsWith("\\\\"))
|
|
63
|
+
return undefined;
|
|
64
|
+
// Reject paths that resolve to home dir, its parent, or root-like dirs.
|
|
65
|
+
// A malicious settings file with autoMemoryDirectory: "~/.." would otherwise
|
|
66
|
+
// redirect bridge writes to /Users/ (or equivalent).
|
|
67
|
+
const home = homedir();
|
|
68
|
+
const homeParent = dirname(home);
|
|
69
|
+
if (normalized === home ||
|
|
70
|
+
normalized === homeParent ||
|
|
71
|
+
normalized === dirname(homeParent)) {
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
return (normalized + sep).normalize("NFC");
|
|
75
|
+
}
|
|
76
|
+
// ---------------------------------------------------------------------------
|
|
77
|
+
// Settings-based override — best local approximation of cc-ts precedence
|
|
78
|
+
// ---------------------------------------------------------------------------
|
|
79
|
+
function readAutoMemDirFromSettings(filePath) {
|
|
80
|
+
try {
|
|
81
|
+
const content = readFileSync(filePath, "utf-8");
|
|
82
|
+
const settings = JSON.parse(content);
|
|
83
|
+
if (typeof settings.autoMemoryDirectory === "string" &&
|
|
84
|
+
settings.autoMemoryDirectory) {
|
|
85
|
+
return settings.autoMemoryDirectory;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
// file doesn't exist or isn't valid JSON
|
|
90
|
+
}
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
function getAutoMemPathSetting(cwd) {
|
|
94
|
+
// Best local approximation of cc-ts precedence:
|
|
95
|
+
// policySettings → flagSettings → localSettings → userSettings
|
|
96
|
+
// We cannot access policySettings or flagSettings from a hook process.
|
|
97
|
+
// Env-var overrides (priority 1 & 2) cover those cases in practice.
|
|
98
|
+
// localSettings: <cwd>/.claude/settings.local.json
|
|
99
|
+
const localDir = readAutoMemDirFromSettings(join(cwd, ".claude", "settings.local.json"));
|
|
100
|
+
if (localDir)
|
|
101
|
+
return validateMemoryPath(localDir, true);
|
|
102
|
+
// userSettings: ~/.claude/settings.json
|
|
103
|
+
const userDir = readAutoMemDirFromSettings(join(getClaudeConfigHome(), "settings.json"));
|
|
104
|
+
if (userDir)
|
|
105
|
+
return validateMemoryPath(userDir, true);
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
// ---------------------------------------------------------------------------
|
|
109
|
+
// Full override via env var
|
|
110
|
+
// ---------------------------------------------------------------------------
|
|
111
|
+
function getAutoMemPathOverride() {
|
|
112
|
+
const raw = process.env.CLAUDE_COWORK_MEMORY_PATH_OVERRIDE;
|
|
113
|
+
return validateMemoryPath(raw, false);
|
|
114
|
+
}
|
|
115
|
+
// ---------------------------------------------------------------------------
|
|
116
|
+
// Canonical git root — walk up, follow worktree chain
|
|
117
|
+
// Ported from cc-ts/utils/git.ts
|
|
118
|
+
// ---------------------------------------------------------------------------
|
|
119
|
+
function findGitRoot(startPath) {
|
|
120
|
+
let current = resolve(startPath);
|
|
121
|
+
const root = current.substring(0, current.indexOf(sep) + 1) || sep;
|
|
122
|
+
while (current !== root) {
|
|
123
|
+
try {
|
|
124
|
+
const gitPath = join(current, ".git");
|
|
125
|
+
const st = statSync(gitPath);
|
|
126
|
+
if (st.isDirectory() || st.isFile()) {
|
|
127
|
+
return current.normalize("NFC");
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
// .git doesn't exist here
|
|
132
|
+
}
|
|
133
|
+
const parent = dirname(current);
|
|
134
|
+
if (parent === current)
|
|
135
|
+
break;
|
|
136
|
+
current = parent;
|
|
137
|
+
}
|
|
138
|
+
// Check root
|
|
139
|
+
try {
|
|
140
|
+
const st = statSync(join(root, ".git"));
|
|
141
|
+
if (st.isDirectory() || st.isFile()) {
|
|
142
|
+
return root.normalize("NFC");
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
// not in git
|
|
147
|
+
}
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
function resolveCanonicalRoot(gitRoot) {
|
|
151
|
+
try {
|
|
152
|
+
const gitContent = readFileSync(join(gitRoot, ".git"), "utf-8").trim();
|
|
153
|
+
if (!gitContent.startsWith("gitdir:")) {
|
|
154
|
+
return gitRoot;
|
|
155
|
+
}
|
|
156
|
+
const worktreeGitDir = resolve(gitRoot, gitContent.slice("gitdir:".length).trim());
|
|
157
|
+
// commondir → shared .git directory. Submodules have none → falls through.
|
|
158
|
+
const commonDir = resolve(worktreeGitDir, readFileSync(join(worktreeGitDir, "commondir"), "utf-8").trim());
|
|
159
|
+
// Security: validate worktree chain structure
|
|
160
|
+
if (resolve(dirname(worktreeGitDir)) !== join(commonDir, "worktrees")) {
|
|
161
|
+
return gitRoot;
|
|
162
|
+
}
|
|
163
|
+
const backlink = realpathSync(readFileSync(join(worktreeGitDir, "gitdir"), "utf-8").trim());
|
|
164
|
+
if (backlink !== join(realpathSync(gitRoot), ".git")) {
|
|
165
|
+
return gitRoot;
|
|
166
|
+
}
|
|
167
|
+
// Bare-repo worktrees: common dir isn't inside a working directory
|
|
168
|
+
if (basename(commonDir) !== ".git") {
|
|
169
|
+
return commonDir.normalize("NFC");
|
|
170
|
+
}
|
|
171
|
+
return dirname(commonDir).normalize("NFC");
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
return gitRoot;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
export function findCanonicalGitRoot(startPath) {
|
|
178
|
+
const root = findGitRoot(startPath);
|
|
179
|
+
if (!root)
|
|
180
|
+
return null;
|
|
181
|
+
return resolveCanonicalRoot(root);
|
|
182
|
+
}
|
|
183
|
+
// ---------------------------------------------------------------------------
|
|
184
|
+
// Public API
|
|
185
|
+
// ---------------------------------------------------------------------------
|
|
186
|
+
/**
|
|
187
|
+
* Resolve the Claude Code auto-memory directory for a given project cwd.
|
|
188
|
+
* Matches cc-ts resolution priority:
|
|
189
|
+
* 1. CLAUDE_COWORK_MEMORY_PATH_OVERRIDE env var
|
|
190
|
+
* 2. autoMemoryDirectory from settings (local → user)
|
|
191
|
+
* 3. <memoryBase>/projects/<sanitized-git-root>/memory/
|
|
192
|
+
*/
|
|
193
|
+
export function getCcMemoryDir(cwd) {
|
|
194
|
+
// Priority 1: full path override
|
|
195
|
+
const override = getAutoMemPathOverride();
|
|
196
|
+
if (override)
|
|
197
|
+
return override;
|
|
198
|
+
// Priority 2: settings override
|
|
199
|
+
const setting = getAutoMemPathSetting(cwd);
|
|
200
|
+
if (setting)
|
|
201
|
+
return setting;
|
|
202
|
+
// Priority 3: computed from canonical git root
|
|
203
|
+
const gitRoot = findCanonicalGitRoot(cwd) ?? cwd;
|
|
204
|
+
const projectsDir = join(getMemoryBaseDir(), "projects");
|
|
205
|
+
return (join(projectsDir, sanitizePath(gitRoot), "memory") + sep).normalize("NFC");
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Check if a file path is inside a cc-ts memory directory.
|
|
209
|
+
* Takes pre-resolved memoryDir for efficiency and clarity.
|
|
210
|
+
*/
|
|
211
|
+
export function isCcMemoryPath(filePath, memoryDir) {
|
|
212
|
+
return filePath.startsWith(memoryDir);
|
|
213
|
+
}
|
|
214
|
+
//# sourceMappingURL=memory-path.js.map
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Schema for the JSON that Claude Code pipes to hook commands via stdin.
|
|
4
|
+
* Matches upstream: cc-ts/entrypoints/sdk/coreSchemas.ts
|
|
5
|
+
*/
|
|
6
|
+
export declare const BaseHookInputSchema: z.ZodObject<{
|
|
7
|
+
session_id: z.ZodString;
|
|
8
|
+
transcript_path: z.ZodString;
|
|
9
|
+
cwd: z.ZodString;
|
|
10
|
+
permission_mode: z.ZodOptional<z.ZodString>;
|
|
11
|
+
agent_id: z.ZodOptional<z.ZodString>;
|
|
12
|
+
agent_type: z.ZodOptional<z.ZodString>;
|
|
13
|
+
}, "strip", z.ZodTypeAny, {
|
|
14
|
+
session_id: string;
|
|
15
|
+
transcript_path: string;
|
|
16
|
+
cwd: string;
|
|
17
|
+
permission_mode?: string | undefined;
|
|
18
|
+
agent_id?: string | undefined;
|
|
19
|
+
agent_type?: string | undefined;
|
|
20
|
+
}, {
|
|
21
|
+
session_id: string;
|
|
22
|
+
transcript_path: string;
|
|
23
|
+
cwd: string;
|
|
24
|
+
permission_mode?: string | undefined;
|
|
25
|
+
agent_id?: string | undefined;
|
|
26
|
+
agent_type?: string | undefined;
|
|
27
|
+
}>;
|
|
28
|
+
export declare const PostToolUseHookInputSchema: z.ZodObject<{
|
|
29
|
+
session_id: z.ZodString;
|
|
30
|
+
transcript_path: z.ZodString;
|
|
31
|
+
cwd: z.ZodString;
|
|
32
|
+
permission_mode: z.ZodOptional<z.ZodString>;
|
|
33
|
+
agent_id: z.ZodOptional<z.ZodString>;
|
|
34
|
+
agent_type: z.ZodOptional<z.ZodString>;
|
|
35
|
+
} & {
|
|
36
|
+
hook_event_name: z.ZodLiteral<"PostToolUse">;
|
|
37
|
+
tool_name: z.ZodString;
|
|
38
|
+
tool_input: z.ZodUnknown;
|
|
39
|
+
tool_response: z.ZodUnknown;
|
|
40
|
+
tool_use_id: z.ZodString;
|
|
41
|
+
}, "strip", z.ZodTypeAny, {
|
|
42
|
+
session_id: string;
|
|
43
|
+
transcript_path: string;
|
|
44
|
+
cwd: string;
|
|
45
|
+
hook_event_name: "PostToolUse";
|
|
46
|
+
tool_name: string;
|
|
47
|
+
tool_use_id: string;
|
|
48
|
+
permission_mode?: string | undefined;
|
|
49
|
+
agent_id?: string | undefined;
|
|
50
|
+
agent_type?: string | undefined;
|
|
51
|
+
tool_input?: unknown;
|
|
52
|
+
tool_response?: unknown;
|
|
53
|
+
}, {
|
|
54
|
+
session_id: string;
|
|
55
|
+
transcript_path: string;
|
|
56
|
+
cwd: string;
|
|
57
|
+
hook_event_name: "PostToolUse";
|
|
58
|
+
tool_name: string;
|
|
59
|
+
tool_use_id: string;
|
|
60
|
+
permission_mode?: string | undefined;
|
|
61
|
+
agent_id?: string | undefined;
|
|
62
|
+
agent_type?: string | undefined;
|
|
63
|
+
tool_input?: unknown;
|
|
64
|
+
tool_response?: unknown;
|
|
65
|
+
}>;
|
|
66
|
+
export declare const StopHookInputSchema: z.ZodObject<{
|
|
67
|
+
session_id: z.ZodString;
|
|
68
|
+
transcript_path: z.ZodString;
|
|
69
|
+
cwd: z.ZodString;
|
|
70
|
+
permission_mode: z.ZodOptional<z.ZodString>;
|
|
71
|
+
agent_id: z.ZodOptional<z.ZodString>;
|
|
72
|
+
agent_type: z.ZodOptional<z.ZodString>;
|
|
73
|
+
} & {
|
|
74
|
+
hook_event_name: z.ZodLiteral<"Stop">;
|
|
75
|
+
stop_hook_active: z.ZodBoolean;
|
|
76
|
+
last_assistant_message: z.ZodOptional<z.ZodString>;
|
|
77
|
+
}, "strip", z.ZodTypeAny, {
|
|
78
|
+
session_id: string;
|
|
79
|
+
transcript_path: string;
|
|
80
|
+
cwd: string;
|
|
81
|
+
hook_event_name: "Stop";
|
|
82
|
+
stop_hook_active: boolean;
|
|
83
|
+
permission_mode?: string | undefined;
|
|
84
|
+
agent_id?: string | undefined;
|
|
85
|
+
agent_type?: string | undefined;
|
|
86
|
+
last_assistant_message?: string | undefined;
|
|
87
|
+
}, {
|
|
88
|
+
session_id: string;
|
|
89
|
+
transcript_path: string;
|
|
90
|
+
cwd: string;
|
|
91
|
+
hook_event_name: "Stop";
|
|
92
|
+
stop_hook_active: boolean;
|
|
93
|
+
permission_mode?: string | undefined;
|
|
94
|
+
agent_id?: string | undefined;
|
|
95
|
+
agent_type?: string | undefined;
|
|
96
|
+
last_assistant_message?: string | undefined;
|
|
97
|
+
}>;
|
|
98
|
+
export declare const UserPromptSubmitHookInputSchema: z.ZodObject<{
|
|
99
|
+
session_id: z.ZodString;
|
|
100
|
+
transcript_path: z.ZodString;
|
|
101
|
+
cwd: z.ZodString;
|
|
102
|
+
permission_mode: z.ZodOptional<z.ZodString>;
|
|
103
|
+
agent_id: z.ZodOptional<z.ZodString>;
|
|
104
|
+
agent_type: z.ZodOptional<z.ZodString>;
|
|
105
|
+
} & {
|
|
106
|
+
hook_event_name: z.ZodLiteral<"UserPromptSubmit">;
|
|
107
|
+
prompt: z.ZodString;
|
|
108
|
+
}, "strip", z.ZodTypeAny, {
|
|
109
|
+
session_id: string;
|
|
110
|
+
transcript_path: string;
|
|
111
|
+
cwd: string;
|
|
112
|
+
hook_event_name: "UserPromptSubmit";
|
|
113
|
+
prompt: string;
|
|
114
|
+
permission_mode?: string | undefined;
|
|
115
|
+
agent_id?: string | undefined;
|
|
116
|
+
agent_type?: string | undefined;
|
|
117
|
+
}, {
|
|
118
|
+
session_id: string;
|
|
119
|
+
transcript_path: string;
|
|
120
|
+
cwd: string;
|
|
121
|
+
hook_event_name: "UserPromptSubmit";
|
|
122
|
+
prompt: string;
|
|
123
|
+
permission_mode?: string | undefined;
|
|
124
|
+
agent_id?: string | undefined;
|
|
125
|
+
agent_type?: string | undefined;
|
|
126
|
+
}>;
|
|
127
|
+
export type PostToolUseHookInput = z.infer<typeof PostToolUseHookInputSchema>;
|
|
128
|
+
export type StopHookInput = z.infer<typeof StopHookInputSchema>;
|
|
129
|
+
export type UserPromptSubmitHookInput = z.infer<typeof UserPromptSubmitHookInputSchema>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Schema for the JSON that Claude Code pipes to hook commands via stdin.
|
|
4
|
+
* Matches upstream: cc-ts/entrypoints/sdk/coreSchemas.ts
|
|
5
|
+
*/
|
|
6
|
+
export const BaseHookInputSchema = z.object({
|
|
7
|
+
session_id: z.string(),
|
|
8
|
+
transcript_path: z.string(),
|
|
9
|
+
cwd: z.string(),
|
|
10
|
+
permission_mode: z.string().optional(),
|
|
11
|
+
agent_id: z.string().optional(),
|
|
12
|
+
agent_type: z.string().optional(),
|
|
13
|
+
});
|
|
14
|
+
export const PostToolUseHookInputSchema = BaseHookInputSchema.extend({
|
|
15
|
+
hook_event_name: z.literal("PostToolUse"),
|
|
16
|
+
tool_name: z.string(),
|
|
17
|
+
tool_input: z.unknown(),
|
|
18
|
+
tool_response: z.unknown(),
|
|
19
|
+
tool_use_id: z.string(),
|
|
20
|
+
});
|
|
21
|
+
export const StopHookInputSchema = BaseHookInputSchema.extend({
|
|
22
|
+
hook_event_name: z.literal("Stop"),
|
|
23
|
+
stop_hook_active: z.boolean(),
|
|
24
|
+
last_assistant_message: z.string().optional(),
|
|
25
|
+
});
|
|
26
|
+
export const UserPromptSubmitHookInputSchema = BaseHookInputSchema.extend({
|
|
27
|
+
hook_event_name: z.literal("UserPromptSubmit"),
|
|
28
|
+
prompt: z.string(),
|
|
29
|
+
});
|
|
30
|
+
//# sourceMappingURL=cc-hook-input.js.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export interface BridgeCommandHook {
|
|
2
|
+
type: "command";
|
|
3
|
+
command: string;
|
|
4
|
+
async: boolean;
|
|
5
|
+
timeout: number;
|
|
6
|
+
}
|
|
7
|
+
export interface BridgeHookEntry {
|
|
8
|
+
matcher?: string;
|
|
9
|
+
hooks: BridgeCommandHook[];
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Read settings.json as a raw JSON object. Returns empty object if missing.
|
|
13
|
+
* Never validates with Zod — preserves all upstream fields verbatim.
|
|
14
|
+
*/
|
|
15
|
+
export declare function readSettingsRaw(path: string): Record<string, unknown>;
|
|
16
|
+
/**
|
|
17
|
+
* Write settings.json, preserving formatting.
|
|
18
|
+
* Creates parent directories if needed.
|
|
19
|
+
*/
|
|
20
|
+
export declare function writeSettingsRaw(path: string, data: Record<string, unknown>): void;
|
|
21
|
+
/**
|
|
22
|
+
* Backup settings.json before modification.
|
|
23
|
+
*/
|
|
24
|
+
export declare function backupSettings(path: string): string;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync, copyFileSync, mkdirSync, } from "node:fs";
|
|
2
|
+
import { dirname } from "node:path";
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// Raw JSON read/write — preserves all unknown fields
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
/**
|
|
7
|
+
* Read settings.json as a raw JSON object. Returns empty object if missing.
|
|
8
|
+
* Never validates with Zod — preserves all upstream fields verbatim.
|
|
9
|
+
*/
|
|
10
|
+
export function readSettingsRaw(path) {
|
|
11
|
+
if (!existsSync(path)) {
|
|
12
|
+
return {};
|
|
13
|
+
}
|
|
14
|
+
const content = readFileSync(path, "utf-8");
|
|
15
|
+
return JSON.parse(content);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Write settings.json, preserving formatting.
|
|
19
|
+
* Creates parent directories if needed.
|
|
20
|
+
*/
|
|
21
|
+
export function writeSettingsRaw(path, data) {
|
|
22
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
23
|
+
writeFileSync(path, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Backup settings.json before modification.
|
|
27
|
+
*/
|
|
28
|
+
export function backupSettings(path) {
|
|
29
|
+
const backupPath = path + ".brv-backup";
|
|
30
|
+
if (existsSync(path)) {
|
|
31
|
+
copyFileSync(path, backupPath);
|
|
32
|
+
}
|
|
33
|
+
return backupPath;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=cc-settings.js.map
|
package/dist/stdin.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ZodSchema } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Read all data from stdin, parse as JSON, and validate with a Zod schema.
|
|
4
|
+
* Used by hook commands (ingest, sync) to read the JSON that Claude Code
|
|
5
|
+
* pipes to hook processes.
|
|
6
|
+
*/
|
|
7
|
+
export declare function readStdinJson<T>(schema: ZodSchema<T>): Promise<T>;
|
package/dist/stdin.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read all data from stdin, parse as JSON, and validate with a Zod schema.
|
|
3
|
+
* Used by hook commands (ingest, sync) to read the JSON that Claude Code
|
|
4
|
+
* pipes to hook processes.
|
|
5
|
+
*/
|
|
6
|
+
export async function readStdinJson(schema) {
|
|
7
|
+
const chunks = [];
|
|
8
|
+
for await (const chunk of process.stdin) {
|
|
9
|
+
chunks.push(chunk);
|
|
10
|
+
}
|
|
11
|
+
const raw = Buffer.concat(chunks).toString("utf8").trim();
|
|
12
|
+
if (!raw) {
|
|
13
|
+
throw new Error("No input received on stdin");
|
|
14
|
+
}
|
|
15
|
+
let parsed;
|
|
16
|
+
try {
|
|
17
|
+
parsed = JSON.parse(raw);
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
throw new Error(`Invalid JSON on stdin: ${raw.slice(0, 200)}`);
|
|
21
|
+
}
|
|
22
|
+
return schema.parse(parsed);
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=stdin.js.map
|
package/package.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@byterover/claude-plugin",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Native bridge between ByteRover context engine and Claude Code — enriches Claude's auto-memory with BM25-ranked knowledge from brv context tree",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"brv-claude-plugin": "./dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "./dist/cli.js",
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "rm -rf dist && tsc",
|
|
12
|
+
"dev": "tsx src/cli.ts",
|
|
13
|
+
"test": "vitest run --dir test",
|
|
14
|
+
"typecheck": "tsc --noEmit",
|
|
15
|
+
"lint": "eslint src/",
|
|
16
|
+
"clean": "rm -rf dist",
|
|
17
|
+
"prepublishOnly": "npm run build && npm test"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist/**/*.js",
|
|
21
|
+
"dist/**/*.d.ts",
|
|
22
|
+
"README.md",
|
|
23
|
+
"LICENSE"
|
|
24
|
+
],
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@byterover/brv-bridge": "^1.0.2",
|
|
27
|
+
"commander": "^13.0.0",
|
|
28
|
+
"js-yaml": "^4.1.0",
|
|
29
|
+
"picocolors": "^1.1.0",
|
|
30
|
+
"zod": "^3.24.0"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/js-yaml": "^4.0.0",
|
|
34
|
+
"@types/node": "^22.0.0",
|
|
35
|
+
"tsx": "^4.19.0",
|
|
36
|
+
"typescript": "^5.7.0",
|
|
37
|
+
"vitest": "^3.0.0"
|
|
38
|
+
},
|
|
39
|
+
"keywords": [
|
|
40
|
+
"byterover",
|
|
41
|
+
"claude-code",
|
|
42
|
+
"memory",
|
|
43
|
+
"context-engine",
|
|
44
|
+
"bridge"
|
|
45
|
+
],
|
|
46
|
+
"author": "ByteRover",
|
|
47
|
+
"license": "Elastic-2.0",
|
|
48
|
+
"engines": {
|
|
49
|
+
"node": ">=20"
|
|
50
|
+
},
|
|
51
|
+
"repository": {
|
|
52
|
+
"type": "git",
|
|
53
|
+
"url": "git+https://github.com/campfirein/brv-claude-plugin.git"
|
|
54
|
+
},
|
|
55
|
+
"homepage": "https://github.com/campfirein/brv-claude-plugin#readme",
|
|
56
|
+
"bugs": {
|
|
57
|
+
"url": "https://github.com/campfirein/brv-claude-plugin/issues"
|
|
58
|
+
},
|
|
59
|
+
"publishConfig": {
|
|
60
|
+
"access": "public"
|
|
61
|
+
}
|
|
62
|
+
}
|