ctxbin 0.1.0 → 0.1.2
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 +22 -22
- package/agent-addon.md +22 -4
- package/dist/agent-addon.md +22 -4
- package/dist/cli.js +21 -12
- package/dist/cli.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/skills/ctxbin/SKILL.md +45 -28
- package/package.json +5 -2
- package/skills/ctxbin/SKILL.md +44 -27
package/README.md
CHANGED
|
@@ -13,11 +13,11 @@ so agents consistently save and load branch context.
|
|
|
13
13
|
- Add: [`agent-addon.md`](agent-addon.md) → copy the block into your project's agent instruction file
|
|
14
14
|
(e.g. `AGENT.md`, `CLAUDE.md`, or any equivalent).
|
|
15
15
|
- Then you can simply ask:
|
|
16
|
-
- “Use ctxbin to save the current context.”
|
|
17
|
-
- “Use ctxbin to load the current context.”
|
|
16
|
+
- “Use npx ctxbin to save the current context.”
|
|
17
|
+
- “Use npx ctxbin to load the current context.”
|
|
18
18
|
|
|
19
19
|
The add-on tells agents how to format context (summary, next steps, decisions) and how to use
|
|
20
|
-
`ctxbin ctx save/load` correctly.
|
|
20
|
+
`npx ctxbin ctx save/load` correctly.
|
|
21
21
|
|
|
22
22
|
## Features
|
|
23
23
|
- Branch-scoped `ctx` keys (auto-inferred from git repo + branch)
|
|
@@ -58,10 +58,10 @@ npx ctxbin init
|
|
|
58
58
|
## Quick usage
|
|
59
59
|
### ctx (branch-scoped, key optional in git repos)
|
|
60
60
|
```bash
|
|
61
|
-
ctxbin ctx save --value "summary / next steps"
|
|
62
|
-
ctxbin ctx load
|
|
63
|
-
ctxbin ctx list
|
|
64
|
-
ctxbin ctx delete
|
|
61
|
+
npx ctxbin ctx save --value "summary / next steps"
|
|
62
|
+
npx ctxbin ctx load
|
|
63
|
+
npx ctxbin ctx list
|
|
64
|
+
npx ctxbin ctx delete
|
|
65
65
|
```
|
|
66
66
|
|
|
67
67
|
When the key is omitted, ctxbin infers it **only inside a git repository**:
|
|
@@ -73,42 +73,42 @@ branch = git rev-parse --abbrev-ref HEAD
|
|
|
73
73
|
|
|
74
74
|
Explicit key example (useful outside git repos; not recommended for normal use):
|
|
75
75
|
```bash
|
|
76
|
-
ctxbin ctx save my-project/main --value "summary / next steps"
|
|
77
|
-
ctxbin ctx load my-project/main
|
|
78
|
-
ctxbin ctx delete my-project/main
|
|
76
|
+
npx ctxbin ctx save my-project/main --value "summary / next steps"
|
|
77
|
+
npx ctxbin ctx load my-project/main
|
|
78
|
+
npx ctxbin ctx delete my-project/main
|
|
79
79
|
```
|
|
80
80
|
|
|
81
81
|
### agent (string-only)
|
|
82
82
|
```bash
|
|
83
|
-
ctxbin agent save reviewer --value "# Agent role"
|
|
84
|
-
ctxbin agent load reviewer
|
|
85
|
-
ctxbin agent list
|
|
86
|
-
ctxbin agent delete reviewer
|
|
83
|
+
npx ctxbin agent save reviewer --value "# Agent role"
|
|
84
|
+
npx ctxbin agent load reviewer
|
|
85
|
+
npx ctxbin agent list
|
|
86
|
+
npx ctxbin agent delete reviewer
|
|
87
87
|
```
|
|
88
88
|
|
|
89
89
|
### skill (string, skillpack, or skillref)
|
|
90
90
|
```bash
|
|
91
|
-
ctxbin skill save my-skill --value "# Skill markdown"
|
|
92
|
-
ctxbin skill load my-skill
|
|
93
|
-
ctxbin skill list
|
|
94
|
-
ctxbin skill delete my-skill
|
|
91
|
+
npx ctxbin skill save my-skill --value "# Skill markdown"
|
|
92
|
+
npx ctxbin skill load my-skill
|
|
93
|
+
npx ctxbin skill list
|
|
94
|
+
npx ctxbin skill delete my-skill
|
|
95
95
|
```
|
|
96
96
|
|
|
97
97
|
Skillpack (directory bundle):
|
|
98
98
|
```bash
|
|
99
|
-
ctxbin skill save fp-pack --dir ./skills/fp-pack
|
|
100
|
-
ctxbin skill load fp-pack --dir ./tmp/fp-pack
|
|
99
|
+
npx ctxbin skill save fp-pack --dir ./skills/fp-pack
|
|
100
|
+
npx ctxbin skill load fp-pack --dir ./tmp/fp-pack
|
|
101
101
|
```
|
|
102
102
|
|
|
103
103
|
Skillref (GitHub directory reference):
|
|
104
104
|
```bash
|
|
105
105
|
# Track default branch
|
|
106
|
-
ctxbin skill save fp-pack \
|
|
106
|
+
npx ctxbin skill save fp-pack \
|
|
107
107
|
--url https://github.com/OWNER/REPO \
|
|
108
108
|
--path skills/fp-pack
|
|
109
109
|
|
|
110
110
|
# Pin to a commit
|
|
111
|
-
ctxbin skill save fp-pack \
|
|
111
|
+
npx ctxbin skill save fp-pack \
|
|
112
112
|
--url https://github.com/OWNER/REPO \
|
|
113
113
|
--ref <40-hex-commit-sha> \
|
|
114
114
|
--path skills/fp-pack
|
package/agent-addon.md
CHANGED
|
@@ -6,7 +6,8 @@ Copy/paste the block between the markers below into your AGENT.md / CLAUDE.md.
|
|
|
6
6
|
|
|
7
7
|
## ctxbin (Context Persistence)
|
|
8
8
|
|
|
9
|
-
Use `ctxbin
|
|
9
|
+
Use `npx ctxbin ...` when running commands in agent workflows.
|
|
10
|
+
Use `npx ctxbin ctx save` to store **branch‑scoped context** so the next agent can continue without re‑explanation.
|
|
10
11
|
> Tip: `npx ctxbin skill load ctxbin` prints the bundled ctxbin skill text
|
|
11
12
|
> so agents can reference the full ctxbin guidance.
|
|
12
13
|
|
|
@@ -19,19 +20,36 @@ branch = git rev-parse --abbrev-ref HEAD
|
|
|
19
20
|
|
|
20
21
|
### Save context (preferred)
|
|
21
22
|
```bash
|
|
22
|
-
ctxbin ctx save --value "<summary + next steps + decisions>"
|
|
23
|
+
npx ctxbin ctx save --value "<summary + next steps + decisions>"
|
|
23
24
|
```
|
|
24
25
|
|
|
25
26
|
### Save via stdin
|
|
26
27
|
```bash
|
|
27
|
-
echo "<context>" | ctxbin ctx save
|
|
28
|
+
echo "<context>" | npx ctxbin ctx save
|
|
28
29
|
```
|
|
29
30
|
|
|
30
31
|
### Load context
|
|
31
32
|
```bash
|
|
32
|
-
ctxbin ctx load
|
|
33
|
+
npx ctxbin ctx load
|
|
33
34
|
```
|
|
34
35
|
|
|
36
|
+
### If load returns NOT_FOUND
|
|
37
|
+
If you see `CTXBIN_ERR NOT_FOUND: no value for ctx:<project>/<branch>`, it means no context was saved yet.
|
|
38
|
+
Tell the user and suggest one of:
|
|
39
|
+
- Ask them to run `npx ctxbin ctx save --value "<summary + next steps>"`, or
|
|
40
|
+
- Ask them to provide the current context directly.
|
|
41
|
+
|
|
42
|
+
### If load returns NOT_IN_GIT
|
|
43
|
+
If you see `CTXBIN_ERR NOT_IN_GIT`, the command was run outside a git repository.
|
|
44
|
+
Tell the user to run it inside the project repo, or use an explicit key:
|
|
45
|
+
```bash
|
|
46
|
+
npx ctxbin ctx load <project>/<branch>
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### If load returns INVALID_INPUT
|
|
50
|
+
If you see `CTXBIN_ERR INVALID_INPUT`, check the command flags.
|
|
51
|
+
For `ctx load`, do not pass `--value`, `--file`, or other input flags.
|
|
52
|
+
|
|
35
53
|
### What to include in ctx
|
|
36
54
|
- What changed (summary)
|
|
37
55
|
- What remains (next steps)
|
package/dist/agent-addon.md
CHANGED
|
@@ -6,7 +6,8 @@ Copy/paste the block between the markers below into your AGENT.md / CLAUDE.md.
|
|
|
6
6
|
|
|
7
7
|
## ctxbin (Context Persistence)
|
|
8
8
|
|
|
9
|
-
Use `ctxbin
|
|
9
|
+
Use `npx ctxbin ...` when running commands in agent workflows.
|
|
10
|
+
Use `npx ctxbin ctx save` to store **branch‑scoped context** so the next agent can continue without re‑explanation.
|
|
10
11
|
> Tip: `npx ctxbin skill load ctxbin` prints the bundled ctxbin skill text
|
|
11
12
|
> so agents can reference the full ctxbin guidance.
|
|
12
13
|
|
|
@@ -19,19 +20,36 @@ branch = git rev-parse --abbrev-ref HEAD
|
|
|
19
20
|
|
|
20
21
|
### Save context (preferred)
|
|
21
22
|
```bash
|
|
22
|
-
ctxbin ctx save --value "<summary + next steps + decisions>"
|
|
23
|
+
npx ctxbin ctx save --value "<summary + next steps + decisions>"
|
|
23
24
|
```
|
|
24
25
|
|
|
25
26
|
### Save via stdin
|
|
26
27
|
```bash
|
|
27
|
-
echo "<context>" | ctxbin ctx save
|
|
28
|
+
echo "<context>" | npx ctxbin ctx save
|
|
28
29
|
```
|
|
29
30
|
|
|
30
31
|
### Load context
|
|
31
32
|
```bash
|
|
32
|
-
ctxbin ctx load
|
|
33
|
+
npx ctxbin ctx load
|
|
33
34
|
```
|
|
34
35
|
|
|
36
|
+
### If load returns NOT_FOUND
|
|
37
|
+
If you see `CTXBIN_ERR NOT_FOUND: no value for ctx:<project>/<branch>`, it means no context was saved yet.
|
|
38
|
+
Tell the user and suggest one of:
|
|
39
|
+
- Ask them to run `npx ctxbin ctx save --value "<summary + next steps>"`, or
|
|
40
|
+
- Ask them to provide the current context directly.
|
|
41
|
+
|
|
42
|
+
### If load returns NOT_IN_GIT
|
|
43
|
+
If you see `CTXBIN_ERR NOT_IN_GIT`, the command was run outside a git repository.
|
|
44
|
+
Tell the user to run it inside the project repo, or use an explicit key:
|
|
45
|
+
```bash
|
|
46
|
+
npx ctxbin ctx load <project>/<branch>
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### If load returns INVALID_INPUT
|
|
50
|
+
If you see `CTXBIN_ERR INVALID_INPUT`, check the command flags.
|
|
51
|
+
For `ctx load`, do not pass `--value`, `--file`, or other input flags.
|
|
52
|
+
|
|
35
53
|
### What to include in ctx
|
|
36
54
|
- What changed (summary)
|
|
37
55
|
- What remains (next steps)
|
package/dist/cli.js
CHANGED
|
@@ -95,11 +95,25 @@ async function writeConfig(config) {
|
|
|
95
95
|
await import_promises.default.writeFile(configPath, JSON.stringify(payload, null, 2), "utf8");
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
+
// src/constants.ts
|
|
99
|
+
var SKILLPACK_HEADER = "ctxbin-skillpack@1\n";
|
|
100
|
+
var SKILLREF_HEADER = "ctxbin-skillref@1\n";
|
|
101
|
+
var MAX_SKILLPACK_BYTES = 7 * 1024 * 1024;
|
|
102
|
+
var MAX_SKILLREF_DOWNLOAD_BYTES = 20 * 1024 * 1024;
|
|
103
|
+
var MAX_SKILLREF_EXTRACT_BYTES = 100 * 1024 * 1024;
|
|
104
|
+
var MAX_SKILLREF_FILES = 5e3;
|
|
105
|
+
var SKILLREF_CONNECT_TIMEOUT_MS = 5e3;
|
|
106
|
+
var SKILLREF_DOWNLOAD_TIMEOUT_MS = 3e4;
|
|
107
|
+
var DEFAULT_EXCLUDES = [".git", "node_modules", ".DS_Store"];
|
|
108
|
+
var STORE_REQUEST_TIMEOUT_MS = 5e3;
|
|
109
|
+
|
|
98
110
|
// src/store.ts
|
|
99
111
|
function createStore(url, token) {
|
|
100
112
|
const baseUrl = url.replace(/\/$/, "");
|
|
101
113
|
async function command(cmd, ...args) {
|
|
102
114
|
let res;
|
|
115
|
+
const controller = new AbortController();
|
|
116
|
+
const timeout = setTimeout(() => controller.abort(), STORE_REQUEST_TIMEOUT_MS);
|
|
103
117
|
try {
|
|
104
118
|
res = await fetch(baseUrl, {
|
|
105
119
|
method: "POST",
|
|
@@ -107,10 +121,16 @@ function createStore(url, token) {
|
|
|
107
121
|
Authorization: `Bearer ${token}`,
|
|
108
122
|
"Content-Type": "application/json"
|
|
109
123
|
},
|
|
110
|
-
body: JSON.stringify([cmd, ...args])
|
|
124
|
+
body: JSON.stringify([cmd, ...args]),
|
|
125
|
+
signal: controller.signal
|
|
111
126
|
});
|
|
112
127
|
} catch (err) {
|
|
128
|
+
if (controller.signal.aborted) {
|
|
129
|
+
return fail("NETWORK", `store request timed out after ${STORE_REQUEST_TIMEOUT_MS}ms`);
|
|
130
|
+
}
|
|
113
131
|
return fail("NETWORK", `store request failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
132
|
+
} finally {
|
|
133
|
+
clearTimeout(timeout);
|
|
114
134
|
}
|
|
115
135
|
if (!res.ok) {
|
|
116
136
|
const text = await res.text();
|
|
@@ -191,17 +211,6 @@ var import_node_zlib = __toESM(require("zlib"));
|
|
|
191
211
|
var import_promises6 = require("stream/promises");
|
|
192
212
|
var import_tar2 = __toESM(require("tar"));
|
|
193
213
|
|
|
194
|
-
// src/constants.ts
|
|
195
|
-
var SKILLPACK_HEADER = "ctxbin-skillpack@1\n";
|
|
196
|
-
var SKILLREF_HEADER = "ctxbin-skillref@1\n";
|
|
197
|
-
var MAX_SKILLPACK_BYTES = 7 * 1024 * 1024;
|
|
198
|
-
var MAX_SKILLREF_DOWNLOAD_BYTES = 20 * 1024 * 1024;
|
|
199
|
-
var MAX_SKILLREF_EXTRACT_BYTES = 100 * 1024 * 1024;
|
|
200
|
-
var MAX_SKILLREF_FILES = 5e3;
|
|
201
|
-
var SKILLREF_CONNECT_TIMEOUT_MS = 5e3;
|
|
202
|
-
var SKILLREF_DOWNLOAD_TIMEOUT_MS = 3e4;
|
|
203
|
-
var DEFAULT_EXCLUDES = [".git", "node_modules", ".DS_Store"];
|
|
204
|
-
|
|
205
214
|
// src/fs-ops.ts
|
|
206
215
|
var import_promises3 = __toESM(require("fs/promises"));
|
|
207
216
|
var import_node_path3 = __toESM(require("path"));
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts","../src/errors.ts","../src/config.ts","../src/store.ts","../src/git.ts","../src/skillpack.ts","../src/constants.ts","../src/fs-ops.ts","../src/chmod.ts","../src/perm.ts","../src/validators.ts","../src/tar-utils.ts","../src/skillref.ts","../src/value.ts","../src/input.ts"],"sourcesContent":["import { parseArgs } from \"node:util\";\nimport process from \"node:process\";\nimport path from \"node:path\";\nimport fs from \"node:fs/promises\";\nimport { readFileSync } from \"node:fs\";\nimport { formatError, fail } from \"./errors\";\nimport { loadConfig, writeConfig } from \"./config\";\nimport { createStore } from \"./store\";\nimport { inferCtxKey } from \"./git\";\nimport { extractSkillpackToDir } from \"./skillpack\";\nimport { loadSkillrefToDir } from \"./skillref\";\nimport { detectSkillValueType } from \"./value\";\nimport { resolveSaveInput } from \"./input\";\n\nasync function main(): Promise<void> {\n let positionals: string[];\n let values: Record<string, unknown>;\n try {\n ({ positionals, values } = parseArgs({\n args: process.argv.slice(2),\n options: {\n append: { type: \"boolean\" },\n version: { type: \"boolean\", short: \"v\" },\n file: { type: \"string\" },\n value: { type: \"string\" },\n dir: { type: \"string\" },\n url: { type: \"string\" },\n ref: { type: \"string\" },\n path: { type: \"string\" },\n },\n allowPositionals: true,\n }));\n } catch (err) {\n return fail(\"INVALID_INPUT\", err instanceof Error ? err.message : \"invalid arguments\");\n }\n\n const [resource, command, keyArg, ...extra] = positionals;\n if (values.version) {\n process.stdout.write(getVersion() + \"\\n\");\n return;\n }\n if (!resource) {\n return fail(\"INVALID_INPUT\", \"missing resource\");\n }\n\n if (resource === \"init\") {\n if (command || keyArg || extra.length) {\n return fail(\"INVALID_INPUT\", \"init does not accept additional arguments\");\n }\n await runInit();\n return;\n }\n\n if (!command) {\n return fail(\"INVALID_INPUT\", \"missing command\");\n }\n if (extra.length > 0) {\n return fail(\"INVALID_INPUT\", \"too many positional arguments\");\n }\n\n const opts = {\n append: Boolean(values.append),\n file: values.file as string | undefined,\n value: values.value as string | undefined,\n dir: values.dir as string | undefined,\n url: values.url as string | undefined,\n ref: values.ref as string | undefined,\n path: values.path as string | undefined,\n };\n\n let store: ReturnType<typeof createStore>;\n try {\n const storeConfig = await loadConfig();\n store = createStore(storeConfig.url, storeConfig.token);\n } catch (err) {\n if (resource === \"skill\" && command === \"load\" && keyArg === \"ctxbin\") {\n const fallback = await loadBundledSkill();\n if (fallback) {\n process.stdout.write(fallback);\n return;\n }\n }\n throw err;\n }\n\n const hash = resolveHash(resource);\n\n if (command === \"list\") {\n if (keyArg) {\n return fail(\"INVALID_INPUT\", \"list does not accept a key\");\n }\n ensureNoListFlags(opts);\n await handleList(store, resource, hash);\n return;\n }\n\n const key = await resolveKey(resource, keyArg);\n\n switch (command) {\n case \"load\":\n await handleLoad(store, resource, hash, key, opts);\n return;\n case \"save\":\n await handleSave(store, resource, hash, key, opts);\n return;\n case \"delete\":\n await handleDelete(store, resource, hash, key, opts);\n return;\n default:\n return fail(\"INVALID_INPUT\", `unknown command: ${command}`);\n }\n}\n\nasync function runInit(): Promise<void> {\n const readline = await import(\"node:readline/promises\");\n const rl = readline.createInterface({ input: process.stdin, output: process.stdout });\n const url = (await rl.question(\"CTXBIN_STORE_URL: \")).trim();\n const token = (await rl.question(\"CTXBIN_STORE_TOKEN: \")).trim();\n await rl.close();\n\n if (!url || !token) {\n return fail(\"INVALID_INPUT\", \"both URL and token are required\");\n }\n\n await writeConfig({ url, token });\n}\n\nfunction resolveHash(resource: string): \"ctx\" | \"agent\" | \"skill\" {\n if (resource === \"ctx\" || resource === \"agent\" || resource === \"skill\") {\n return resource;\n }\n return fail(\"INVALID_INPUT\", `unknown resource: ${resource}`);\n}\n\nasync function resolveKey(resource: string, keyArg?: string): Promise<string> {\n if (resource === \"ctx\") {\n if (keyArg) return keyArg;\n return inferCtxKey();\n }\n if (!keyArg) {\n return fail(\"MISSING_KEY\", \"key is required\");\n }\n return keyArg;\n}\n\nasync function handleLoad(\n store: ReturnType<typeof createStore>,\n resource: string,\n hash: string,\n key: string,\n opts: {\n append: boolean;\n file?: string;\n value?: string;\n dir?: string;\n url?: string;\n ref?: string;\n path?: string;\n }\n): Promise<void> {\n ensureNoSaveInput(opts, \"load\");\n\n const value = await store.get(hash, key);\n if (value === null) {\n if (resource === \"skill\" && key === \"ctxbin\") {\n const fallback = await loadBundledSkill();\n if (fallback) {\n process.stdout.write(fallback);\n return;\n }\n }\n return fail(\"NOT_FOUND\", `no value for ${hash}:${key}`);\n }\n\n if (resource === \"skill\") {\n const kind = detectSkillValueType(value);\n if (kind === \"string\") {\n if (opts.dir) {\n return fail(\"TYPE_MISMATCH\", \"--dir cannot be used with string values\");\n }\n process.stdout.write(value);\n return;\n }\n if (!opts.dir) {\n return fail(\"TYPE_MISMATCH\", \"--dir is required for skillpack/skillref load\");\n }\n if (kind === \"skillpack\") {\n await extractSkillpackToDir(value, opts.dir);\n return;\n }\n if (kind === \"skillref\") {\n await loadSkillrefToDir(value, opts.dir);\n return;\n }\n }\n\n if (opts.dir) {\n return fail(\"TYPE_MISMATCH\", \"--dir is only valid for skill values\");\n }\n process.stdout.write(value);\n}\n\nasync function handleSave(\n store: ReturnType<typeof createStore>,\n resource: string,\n hash: string,\n key: string,\n opts: {\n append: boolean;\n file?: string;\n value?: string;\n dir?: string;\n url?: string;\n ref?: string;\n path?: string;\n }\n): Promise<void> {\n const input = await resolveSaveInput(resource, opts);\n\n if (opts.append) {\n if (input.kind !== \"string\") {\n return fail(\"INVALID_INPUT\", \"--append only applies to string inputs\");\n }\n const existing = await store.get(hash, key);\n if (resource === \"skill\" && existing && detectSkillValueType(existing) !== \"string\") {\n return fail(\"TYPE_MISMATCH\", \"cannot append to skillpack/skillref values\");\n }\n const merged = existing ? `${existing}\\n\\n${input.value}` : input.value;\n await store.set(hash, key, merged);\n return;\n }\n\n if (input.kind !== \"string\" && resource !== \"skill\") {\n return fail(\"TYPE_MISMATCH\", \"non-string inputs are only valid for skill\" );\n }\n\n await store.set(hash, key, input.value);\n}\n\nasync function handleDelete(\n store: ReturnType<typeof createStore>,\n resource: string,\n hash: string,\n key: string,\n opts: {\n append: boolean;\n file?: string;\n value?: string;\n dir?: string;\n url?: string;\n ref?: string;\n path?: string;\n }\n): Promise<void> {\n ensureNoSaveInput(opts, \"delete\");\n if (opts.dir) {\n return fail(\"INVALID_INPUT\", \"--dir is not valid for delete\");\n }\n await store.delete(hash, key);\n}\n\nasync function handleList(\n store: ReturnType<typeof createStore>,\n resource: string,\n hash: string\n): Promise<void> {\n const entries = await store.list(hash);\n if (entries.length === 0) return;\n\n const lines = entries.map(({ field, value }) => {\n let type = \"--value\";\n if (resource === \"skill\") {\n const kind = detectSkillValueType(value);\n if (kind === \"skillpack\") type = \"--dir\";\n if (kind === \"skillref\") type = \"--url\";\n }\n return `${field}\\t${type}`;\n });\n\n process.stdout.write(lines.join(\"\\n\"));\n}\n\n\nfunction ensureNoSaveInput(\n opts: {\n append: boolean;\n file?: string;\n value?: string;\n dir?: string;\n url?: string;\n ref?: string;\n path?: string;\n },\n command: \"load\" | \"delete\"\n): void {\n if (opts.append || opts.file || opts.value || opts.url || opts.ref || opts.path) {\n return fail(\"INVALID_INPUT\", `${command} does not accept input flags`);\n }\n}\n\nfunction ensureNoListFlags(opts: {\n append: boolean;\n file?: string;\n value?: string;\n dir?: string;\n url?: string;\n ref?: string;\n path?: string;\n}): void {\n if (opts.append || opts.file || opts.value || opts.dir || opts.url || opts.ref || opts.path) {\n return fail(\"INVALID_INPUT\", \"list does not accept input flags\");\n }\n}\n\nmain().catch((err) => {\n process.stderr.write(formatError(err) + \"\\n\");\n process.exit(1);\n});\n\nasync function loadBundledSkill(): Promise<string | null> {\n try {\n const bundled = path.resolve(__dirname, \"skills\", \"ctxbin\", \"SKILL.md\");\n return await fs.readFile(bundled, \"utf8\");\n } catch {\n return null;\n }\n}\n\nfunction getVersion(): string {\n try {\n const pkgPath = path.resolve(__dirname, \"..\", \"package.json\");\n const raw = readFileSync(pkgPath, \"utf8\");\n const data = JSON.parse(raw);\n if (data && typeof data.version === \"string\") {\n return data.version;\n }\n } catch {\n // ignore\n }\n return \"0.0.0\";\n}\n","export type ErrorCode =\n | \"INVALID_INPUT\"\n | \"MISSING_KEY\"\n | \"INVALID_URL\"\n | \"INVALID_REF\"\n | \"INVALID_PATH\"\n | \"NOT_IN_GIT\"\n | \"NOT_FOUND\"\n | \"TYPE_MISMATCH\"\n | \"SIZE_LIMIT\"\n | \"NETWORK\"\n | \"IO\";\n\nexport class CtxbinError extends Error {\n readonly code: ErrorCode;\n\n constructor(code: ErrorCode, message: string) {\n super(message);\n this.code = code;\n }\n}\n\nexport function fail(code: ErrorCode, message: string): never {\n throw new CtxbinError(code, message);\n}\n\nexport function formatError(err: unknown): string {\n if (err instanceof CtxbinError) {\n return `CTXBIN_ERR ${err.code}: ${err.message}`;\n }\n const message = err instanceof Error ? err.message : String(err);\n return `CTXBIN_ERR IO: ${message}`;\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport os from \"node:os\";\nimport { fail } from \"./errors\";\n\nexport interface StoreConfig {\n url: string;\n token: string;\n}\n\nconst ENV_URL = \"CTXBIN_STORE_URL\";\nconst ENV_TOKEN = \"CTXBIN_STORE_TOKEN\";\n\nexport async function loadConfig(): Promise<StoreConfig> {\n const envUrl = process.env[ENV_URL];\n const envToken = process.env[ENV_TOKEN];\n\n if (envUrl || envToken) {\n if (!envUrl || !envToken) {\n return fail(\"INVALID_INPUT\", \"both CTXBIN_STORE_URL and CTXBIN_STORE_TOKEN must be set\");\n }\n return { url: envUrl, token: envToken };\n }\n\n const configPath = path.join(os.homedir(), \".ctxbin\", \"config.json\");\n let raw: string;\n try {\n raw = await fs.readFile(configPath, \"utf8\");\n } catch {\n return fail(\"INVALID_INPUT\", \"missing CTXBIN_STORE_URL/CTXBIN_STORE_TOKEN and no ~/.ctxbin/config.json\");\n }\n\n let parsed: any;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return fail(\"INVALID_INPUT\", \"invalid JSON in ~/.ctxbin/config.json\");\n }\n\n const url = parsed.store_url || parsed.storeUrl || parsed.url;\n const token = parsed.store_token || parsed.storeToken || parsed.token;\n if (!url || !token) {\n return fail(\"INVALID_INPUT\", \"config.json must include store_url and store_token\");\n }\n\n return { url, token };\n}\n\nexport async function writeConfig(config: StoreConfig): Promise<void> {\n const dir = path.join(os.homedir(), \".ctxbin\");\n const configPath = path.join(dir, \"config.json\");\n await fs.mkdir(dir, { recursive: true, mode: 0o700 });\n const payload = {\n store_url: config.url,\n store_token: config.token,\n };\n await fs.writeFile(configPath, JSON.stringify(payload, null, 2), \"utf8\");\n}\n","import { fail } from \"./errors\";\n\nexport interface Store {\n get(hash: string, field: string): Promise<string | null>;\n set(hash: string, field: string, value: string): Promise<void>;\n delete(hash: string, field: string): Promise<void>;\n list(hash: string): Promise<Array<{ field: string; value: string }>>;\n}\n\ninterface UpstashResponse<T> {\n result?: T;\n error?: string;\n}\n\nexport function createStore(url: string, token: string): Store {\n const baseUrl = url.replace(/\\/$/, \"\");\n\n async function command<T>(cmd: string, ...args: string[]): Promise<T> {\n let res: Response;\n try {\n res = await fetch(baseUrl, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify([cmd, ...args]),\n });\n } catch (err) {\n return fail(\"NETWORK\", `store request failed: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n if (!res.ok) {\n const text = await res.text();\n return fail(\"NETWORK\", `store request failed (${res.status}): ${text}`);\n }\n\n let data: UpstashResponse<T>;\n try {\n data = (await res.json()) as UpstashResponse<T>;\n } catch {\n return fail(\"NETWORK\", \"store response was not valid JSON\");\n }\n\n if (data.error) {\n return fail(\"NETWORK\", data.error);\n }\n\n return data.result as T;\n }\n\n return {\n async get(hash, field) {\n const result = await command<string | null>(\"HGET\", hash, field);\n return result ?? null;\n },\n async set(hash, field, value) {\n await command<number>(\"HSET\", hash, field, value);\n },\n async delete(hash, field) {\n await command<number>(\"HDEL\", hash, field);\n },\n async list(hash) {\n const result = await command<unknown>(\"HGETALL\", hash);\n if (!result) return [];\n if (!Array.isArray(result)) {\n return fail(\"NETWORK\", \"store response for HGETALL was not an array\");\n }\n const pairs: Array<{ field: string; value: string }> = [];\n for (let i = 0; i < result.length; i += 2) {\n const field = result[i];\n const value = result[i + 1];\n if (typeof field !== \"string\" || typeof value !== \"string\") {\n return fail(\"NETWORK\", \"store response for HGETALL contained invalid entries\");\n }\n pairs.push({ field, value });\n }\n pairs.sort((a, b) => a.field.localeCompare(b.field));\n return pairs;\n },\n };\n}\n","import { execFile } from \"node:child_process\";\nimport path from \"node:path\";\nimport { promisify } from \"node:util\";\nimport { fail } from \"./errors\";\n\nconst execFileAsync = promisify(execFile);\n\nasync function git(args: string[]): Promise<string> {\n try {\n const { stdout } = await execFileAsync(\"git\", args, { encoding: \"utf8\" });\n return stdout.trim();\n } catch {\n return fail(\"NOT_IN_GIT\", \"not inside a git repository\");\n }\n}\n\nexport async function inferCtxKey(): Promise<string> {\n const root = await git([\"rev-parse\", \"--show-toplevel\"]);\n const branch = await git([\"rev-parse\", \"--abbrev-ref\", \"HEAD\"]);\n const project = path.basename(root);\n if (!project || !branch) {\n return fail(\"NOT_IN_GIT\", \"unable to infer ctx key from git repository\");\n }\n return `${project}/${branch}`;\n}\n","import fs from \"node:fs/promises\";\nimport { createWriteStream } from \"node:fs\";\nimport path from \"node:path\";\nimport os from \"node:os\";\nimport zlib from \"node:zlib\";\nimport { pipeline } from \"node:stream/promises\";\nimport tar from \"tar\";\nimport { DEFAULT_EXCLUDES, MAX_SKILLPACK_BYTES, SKILLPACK_HEADER } from \"./constants\";\nimport { fail } from \"./errors\";\nimport { ensureDir, copyDirContents } from \"./fs-ops\";\nimport { applyNormalizedPermissions } from \"./perm\";\nimport { assertSafeTarPath } from \"./validators\";\nimport { listTarEntries } from \"./tar-utils\";\n\nconst ALLOWED_TYPES = new Set([\"File\", \"Directory\"]);\n\nexport async function createSkillpackFromDir(dirPath: string): Promise<string> {\n const stats = await fs.stat(dirPath).catch(() => null);\n if (!stats || !stats.isDirectory()) {\n return fail(\"INVALID_INPUT\", `--dir is not a directory: ${dirPath}`);\n }\n\n const entries = await collectEntries(dirPath);\n const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), \"ctxbin-skillpack-\"));\n const tarPath = path.join(tmpDir, \"skillpack.tar.gz\");\n\n try {\n const tarStream = tar.c(\n {\n cwd: dirPath,\n portable: true,\n mtime: new Date(0),\n },\n entries\n );\n\n const gzip = zlib.createGzip({ mtime: 0 });\n await pipeline(tarStream, gzip, createWriteStream(tarPath));\n\n const stat = await fs.stat(tarPath);\n if (stat.size > MAX_SKILLPACK_BYTES) {\n return fail(\n \"SIZE_LIMIT\",\n `skillpack tar.gz size ${stat.size} bytes exceeds ${MAX_SKILLPACK_BYTES} bytes`\n );\n }\n\n const data = await fs.readFile(tarPath);\n const b64 = data.toString(\"base64\");\n return SKILLPACK_HEADER + b64;\n } finally {\n await fs.rm(tmpDir, { recursive: true, force: true });\n }\n}\n\nexport async function extractSkillpackToDir(value: string, targetDir: string): Promise<void> {\n const base64 = value.slice(SKILLPACK_HEADER.length);\n let buffer: Buffer;\n try {\n buffer = Buffer.from(base64, \"base64\");\n } catch {\n return fail(\"IO\", \"invalid skillpack base64 data\");\n }\n\n const tmpRoot = await fs.mkdtemp(path.join(os.tmpdir(), \"ctxbin-skillpack-\"));\n const tarPath = path.join(tmpRoot, \"skillpack.tar.gz\");\n await fs.writeFile(tarPath, buffer);\n\n try {\n const entries = await listTarEntries(tarPath);\n const execSet = validateTarEntries(entries);\n\n const extractDir = path.join(tmpRoot, \"extract\");\n await ensureDir(extractDir);\n await tar.x({\n file: tarPath,\n cwd: extractDir,\n preserveOwner: false,\n noMtime: true,\n });\n\n await applyNormalizedPermissions(extractDir, execSet);\n await ensureDir(targetDir);\n await copyDirContents(extractDir, targetDir);\n } finally {\n await fs.rm(tmpRoot, { recursive: true, force: true });\n }\n}\n\nasync function collectEntries(root: string): Promise<string[]> {\n const results: string[] = [];\n\n async function walk(absDir: string, relDir: string): Promise<void> {\n const entries = await fs.readdir(absDir, { withFileTypes: true });\n entries.sort((a, b) => a.name.localeCompare(b.name));\n\n for (const entry of entries) {\n if (DEFAULT_EXCLUDES.includes(entry.name)) {\n if (entry.isDirectory()) {\n continue;\n }\n if (entry.isFile() && entry.name === \".DS_Store\") {\n continue;\n }\n }\n\n const absPath = path.join(absDir, entry.name);\n const relPath = relDir ? path.posix.join(relDir, entry.name) : entry.name;\n const stat = await fs.lstat(absPath);\n if (stat.isSymbolicLink()) {\n return fail(\"IO\", `symlink not allowed in skillpack: ${absPath}`);\n }\n\n if (entry.isDirectory()) {\n results.push(relPath);\n await walk(absPath, relPath);\n continue;\n }\n\n if (entry.isFile()) {\n if (entry.name === \".DS_Store\") {\n continue;\n }\n results.push(relPath);\n continue;\n }\n\n return fail(\"IO\", `unsupported file type in skillpack: ${absPath}`);\n }\n }\n\n await walk(root, \"\");\n results.sort();\n return results;\n}\n\nfunction validateTarEntries(entries: { path: string; type: string; mode: number }[]): Set<string> {\n const execSet = new Set<string>();\n for (const entry of entries) {\n assertSafeTarPath(entry.path);\n if (!ALLOWED_TYPES.has(entry.type)) {\n return fail(\"IO\", `unsupported entry type in skillpack: ${entry.path}`);\n }\n if (entry.type === \"File\" && (entry.mode & 0o111)) {\n execSet.add(entry.path);\n }\n }\n return execSet;\n}\n","export const SKILLPACK_HEADER = \"ctxbin-skillpack@1\\n\";\nexport const SKILLREF_HEADER = \"ctxbin-skillref@1\\n\";\n\nexport const MAX_SKILLPACK_BYTES = 7 * 1024 * 1024;\nexport const MAX_SKILLREF_DOWNLOAD_BYTES = 20 * 1024 * 1024;\nexport const MAX_SKILLREF_EXTRACT_BYTES = 100 * 1024 * 1024;\nexport const MAX_SKILLREF_FILES = 5000;\n\nexport const SKILLREF_CONNECT_TIMEOUT_MS = 5000;\nexport const SKILLREF_DOWNLOAD_TIMEOUT_MS = 30000;\n\nexport const DEFAULT_EXCLUDES = [\".git\", \"node_modules\", \".DS_Store\"];\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { fail } from \"./errors\";\nimport { safeChmod } from \"./chmod\";\n\nexport async function ensureDir(dir: string): Promise<void> {\n await fs.mkdir(dir, { recursive: true });\n}\n\nexport function toPosix(p: string): string {\n return p.split(path.sep).join(\"/\");\n}\n\nexport async function copyDirContents(src: string, dest: string): Promise<void> {\n await ensureDir(dest);\n const entries = await fs.readdir(src, { withFileTypes: true });\n for (const entry of entries) {\n const srcPath = path.join(src, entry.name);\n const destPath = path.join(dest, entry.name);\n if (entry.isDirectory()) {\n await copyDirContents(srcPath, destPath);\n const stat = await fs.stat(srcPath);\n await safeChmod(destPath, stat.mode & 0o777);\n continue;\n }\n if (entry.isFile()) {\n await ensureDir(path.dirname(destPath));\n await fs.copyFile(srcPath, destPath);\n const stat = await fs.stat(srcPath);\n await safeChmod(destPath, stat.mode & 0o777);\n continue;\n }\n return fail(\"IO\", `unsupported file type during copy: ${srcPath}`);\n }\n}\n","import fs from \"node:fs/promises\";\n\nexport async function safeChmod(path: string, mode: number): Promise<void> {\n try {\n await fs.chmod(path, mode);\n } catch (err) {\n if (process.platform === \"win32\") {\n return;\n }\n throw err;\n }\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { fail } from \"./errors\";\nimport { toPosix } from \"./fs-ops\";\nimport { safeChmod } from \"./chmod\";\n\nexport async function applyNormalizedPermissions(root: string, execSet: Set<string>): Promise<void> {\n async function walk(absDir: string): Promise<void> {\n const entries = await fs.readdir(absDir, { withFileTypes: true });\n for (const entry of entries) {\n const absPath = path.join(absDir, entry.name);\n if (entry.isDirectory()) {\n await safeChmod(absPath, 0o755);\n await walk(absPath);\n continue;\n }\n if (entry.isFile()) {\n const rel = toPosix(path.relative(root, absPath));\n const mode = execSet.has(rel) ? 0o755 : 0o644;\n await safeChmod(absPath, mode);\n continue;\n }\n return fail(\"IO\", `unsupported file type after extract: ${absPath}`);\n }\n }\n\n await walk(root);\n}\n","import path from \"node:path\";\nimport { fail } from \"./errors\";\n\nexport function normalizeGithubUrl(input: string): string {\n let url: URL;\n try {\n url = new URL(input);\n } catch {\n return fail(\"INVALID_URL\", \"invalid URL\");\n }\n\n if (url.protocol !== \"https:\") {\n return fail(\"INVALID_URL\", \"URL must use https\");\n }\n if (url.hostname !== \"github.com\") {\n return fail(\"INVALID_URL\", \"only github.com is supported\");\n }\n if (url.search || url.hash) {\n return fail(\"INVALID_URL\", \"URL must not include query or hash\");\n }\n\n const parts = url.pathname.split(\"/\").filter(Boolean);\n if (parts.length !== 2) {\n return fail(\"INVALID_URL\", \"URL must be https://github.com/<owner>/<repo>\");\n }\n const owner = parts[0];\n let repo = parts[1];\n if (repo.endsWith(\".git\")) {\n repo = repo.slice(0, -4);\n }\n if (!owner || !repo) {\n return fail(\"INVALID_URL\", \"URL must be https://github.com/<owner>/<repo>\");\n }\n\n return `https://github.com/${owner}/${repo}`;\n}\n\nexport function validateCommitSha(ref: string): string {\n if (!/^[0-9a-f]{40}$/.test(ref)) {\n return fail(\"INVALID_REF\", \"ref must be a 40-hex commit SHA\");\n }\n return ref;\n}\n\nexport function normalizeSkillPath(input: string): string {\n const trimmed = input.trim();\n if (!trimmed) {\n return fail(\"INVALID_PATH\", \"path must be a non-empty directory path\");\n }\n const cleaned = trimmed.replace(/\\\\/g, \"/\");\n if (cleaned.startsWith(\"/\")) {\n return fail(\"INVALID_PATH\", \"path must be relative, not absolute\");\n }\n const normalized = path.posix.normalize(cleaned).replace(/^\\.\\//, \"\");\n if (normalized === \".\" || normalized === \"\") {\n return fail(\"INVALID_PATH\", \"path must be a non-empty directory path\");\n }\n if (normalized.startsWith(\"../\") || normalized.includes(\"/../\") || normalized === \"..\") {\n return fail(\"INVALID_PATH\", \"path must not include .. segments\");\n }\n if (normalized.endsWith(\"/\")) {\n return normalized.slice(0, -1);\n }\n return normalized;\n}\n\nexport function assertSafeTarPath(entryPath: string): void {\n const cleaned = entryPath.replace(/\\\\/g, \"/\");\n if (cleaned.startsWith(\"/\")) {\n return fail(\"INVALID_PATH\", `tar entry path must be relative: ${entryPath}`);\n }\n const normalized = path.posix.normalize(cleaned);\n if (normalized.startsWith(\"../\") || normalized === \"..\" || normalized.includes(\"/../\")) {\n return fail(\"INVALID_PATH\", `tar entry path contains traversal: ${entryPath}`);\n }\n}\n","import tar from \"tar\";\n\nexport interface TarEntryInfo {\n path: string;\n type: string;\n size: number;\n mode: number;\n}\n\nexport async function listTarEntries(file: string): Promise<TarEntryInfo[]> {\n const entries: TarEntryInfo[] = [];\n await tar.t({\n file,\n onentry(entry) {\n entries.push({\n path: entry.path,\n type: entry.type,\n size: entry.size ?? 0,\n mode: entry.mode ?? 0,\n });\n },\n });\n return entries;\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport os from \"node:os\";\nimport { createWriteStream } from \"node:fs\";\nimport { SKILLREF_HEADER, MAX_SKILLREF_DOWNLOAD_BYTES, MAX_SKILLREF_EXTRACT_BYTES, MAX_SKILLREF_FILES, SKILLREF_CONNECT_TIMEOUT_MS, SKILLREF_DOWNLOAD_TIMEOUT_MS } from \"./constants\";\nimport { fail, CtxbinError } from \"./errors\";\nimport { normalizeGithubUrl, normalizeSkillPath, validateCommitSha, assertSafeTarPath } from \"./validators\";\nimport { listTarEntries, TarEntryInfo } from \"./tar-utils\";\nimport tar from \"tar\";\nimport { ensureDir, copyDirContents } from \"./fs-ops\";\nimport { applyNormalizedPermissions } from \"./perm\";\n\nconst ALLOWED_TYPES = new Set([\"File\", \"Directory\"]);\n\nexport interface Skillref {\n url: string;\n path: string;\n ref?: string;\n track?: \"default\";\n}\n\nexport function createSkillrefValue(url: string, skillPath: string, ref?: string): string {\n const normalizedUrl = normalizeGithubUrl(url);\n const normalizedPath = normalizeSkillPath(skillPath);\n const payload = ref\n ? JSON.stringify({ url: normalizedUrl, path: normalizedPath, ref: validateCommitSha(ref) })\n : JSON.stringify({ url: normalizedUrl, path: normalizedPath, track: \"default\" });\n return SKILLREF_HEADER + payload;\n}\n\nexport function parseSkillrefValue(value: string): Skillref {\n if (!value.startsWith(SKILLREF_HEADER)) {\n return fail(\"TYPE_MISMATCH\", \"value is not a skillref\");\n }\n const raw = value.slice(SKILLREF_HEADER.length);\n let parsed: any;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return fail(\"IO\", \"invalid skillref payload JSON\");\n }\n if (!parsed || typeof parsed.url !== \"string\" || typeof parsed.path !== \"string\") {\n return fail(\"IO\", \"invalid skillref payload fields\");\n }\n const normalized = {\n url: normalizeGithubUrl(parsed.url),\n path: normalizeSkillPath(parsed.path),\n } satisfies Pick<Skillref, \"url\" | \"path\">;\n\n if (typeof parsed.ref === \"string\") {\n return { ...normalized, ref: validateCommitSha(parsed.ref) };\n }\n\n if (parsed.track === \"default\") {\n return { ...normalized, track: \"default\" };\n }\n\n return fail(\"IO\", \"invalid skillref payload fields\");\n}\n\nexport async function loadSkillrefToDir(value: string, targetDir: string): Promise<void> {\n const skillref = parseSkillrefValue(value);\n const resolvedRef = skillref.ref ?? (await fetchDefaultBranch(skillref.url));\n const tmpRoot = await fs.mkdtemp(path.join(os.tmpdir(), \"ctxbin-skillref-\"));\n const tarPath = path.join(tmpRoot, \"skillref.tar.gz\");\n\n try {\n await downloadArchive(skillref.url, resolvedRef, tarPath);\n\n const entries = await listTarEntries(tarPath).catch(() => fail(\"IO\", \"failed to parse tar archive\"));\n const analysis = analyzeEntries(entries, skillref.path);\n\n const extractDir = path.join(tmpRoot, \"extract\");\n await ensureDir(extractDir);\n\n const stripCount = 1 + skillref.path.split(\"/\").length;\n await tar.x({\n file: tarPath,\n cwd: extractDir,\n preserveOwner: false,\n noMtime: true,\n strip: stripCount,\n filter: (p, entry) => {\n const entryPath = entry?.path ?? p;\n return isUnderPath(entryPath, analysis.prefix, skillref.path);\n },\n });\n\n await applyNormalizedPermissions(extractDir, analysis.execSet);\n await ensureDir(targetDir);\n await copyDirContents(extractDir, targetDir);\n } finally {\n await fs.rm(tmpRoot, { recursive: true, force: true });\n }\n}\n\nasync function downloadArchive(repoUrl: string, ref: string, outPath: string): Promise<void> {\n const { owner, repo } = splitGithubUrl(repoUrl);\n const url = `https://codeload.github.com/${owner}/${repo}/tar.gz/${ref}`;\n const controller = new AbortController();\n const totalTimer = setTimeout(() => controller.abort(), SKILLREF_DOWNLOAD_TIMEOUT_MS);\n let res: Response;\n try {\n res = await fetchWithRedirect(url, 1, controller, [\"github.com\", \"codeload.github.com\"]);\n } catch (err) {\n clearTimeout(totalTimer);\n return fail(\"NETWORK\", `download failed: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n if (!res.ok) {\n clearTimeout(totalTimer);\n const text = await res.text();\n return fail(\"NETWORK\", `download failed (${res.status}): ${text}`);\n }\n if (!res.body) {\n clearTimeout(totalTimer);\n return fail(\"NETWORK\", \"download failed: empty response body\");\n }\n\n const fileStream = createWriteStream(outPath);\n let total = 0;\n let magic = Buffer.alloc(0);\n\n try {\n for await (const chunk of res.body as AsyncIterable<Buffer>) {\n if (magic.length < 2) {\n const needed = 2 - magic.length;\n magic = Buffer.concat([magic, chunk.subarray(0, needed)]);\n if (magic.length === 2) {\n if (magic[0] !== 0x1f || magic[1] !== 0x8b) {\n fileStream.close();\n controller.abort();\n return fail(\"IO\", \"downloaded file is not gzip data\");\n }\n }\n }\n\n total += chunk.length;\n if (total > MAX_SKILLREF_DOWNLOAD_BYTES) {\n fileStream.close();\n controller.abort();\n return fail(\n \"SIZE_LIMIT\",\n `downloaded archive size ${total} exceeds ${MAX_SKILLREF_DOWNLOAD_BYTES} bytes`\n );\n }\n\n fileStream.write(chunk);\n }\n } catch (err) {\n fileStream.close();\n clearTimeout(totalTimer);\n if (err instanceof CtxbinError) {\n throw err;\n }\n return fail(\"NETWORK\", `download failed: ${err instanceof Error ? err.message : String(err)}`);\n } finally {\n clearTimeout(totalTimer);\n }\n\n if (magic.length < 2) {\n fileStream.close();\n return fail(\"IO\", \"downloaded file is incomplete\");\n }\n\n await new Promise<void>((resolve, reject) => {\n fileStream.end(() => resolve());\n fileStream.on(\"error\", reject);\n });\n}\n\nasync function fetchWithRedirect(\n url: string,\n redirectsLeft: number,\n controller: AbortController,\n allowedHosts: string[],\n init?: RequestInit\n): Promise<Response> {\n const connectTimer = setTimeout(() => controller.abort(), SKILLREF_CONNECT_TIMEOUT_MS);\n\n const res = await fetch(url, {\n ...init,\n signal: controller.signal,\n redirect: \"manual\",\n });\n\n clearTimeout(connectTimer);\n\n if (isRedirect(res.status)) {\n if (redirectsLeft <= 0) {\n return fail(\"NETWORK\", \"too many redirects\");\n }\n const location = res.headers.get(\"location\");\n if (!location) {\n return fail(\"NETWORK\", \"redirect without location header\");\n }\n const nextUrl = new URL(location, url).toString();\n const host = new URL(nextUrl).hostname;\n if (!allowedHosts.includes(host)) {\n return fail(\"NETWORK\", `redirected to unsupported host: ${host}`);\n }\n return fetchWithRedirect(nextUrl, redirectsLeft - 1, controller, allowedHosts, init);\n }\n\n return res;\n}\n\nfunction isRedirect(status: number): boolean {\n return [301, 302, 303, 307, 308].includes(status);\n}\n\nfunction splitGithubUrl(repoUrl: string): { owner: string; repo: string } {\n const url = new URL(repoUrl);\n const parts = url.pathname.split(\"/\").filter(Boolean);\n if (parts.length !== 2) {\n return fail(\"INVALID_URL\", \"URL must be https://github.com/<owner>/<repo>\");\n }\n return { owner: parts[0], repo: parts[1] };\n}\n\nasync function fetchDefaultBranch(repoUrl: string): Promise<string> {\n const { owner, repo } = splitGithubUrl(repoUrl);\n const url = `https://api.github.com/repos/${owner}/${repo}`;\n const controller = new AbortController();\n const totalTimer = setTimeout(() => controller.abort(), SKILLREF_DOWNLOAD_TIMEOUT_MS);\n let res: Response;\n try {\n res = await fetchWithRedirect(url, 1, controller, [\"github.com\", \"api.github.com\"], {\n headers: {\n \"User-Agent\": \"ctxbin\",\n Accept: \"application/vnd.github+json\",\n },\n });\n } catch (err) {\n clearTimeout(totalTimer);\n return fail(\"NETWORK\", `default branch lookup failed: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n if (!res.ok) {\n clearTimeout(totalTimer);\n const text = await res.text();\n return fail(\"NETWORK\", `default branch lookup failed (${res.status}): ${text}`);\n }\n\n let data: any;\n try {\n data = await res.json();\n } catch {\n clearTimeout(totalTimer);\n return fail(\"NETWORK\", \"default branch lookup returned invalid JSON\");\n }\n\n clearTimeout(totalTimer);\n if (!data || typeof data.default_branch !== \"string\" || data.default_branch.length === 0) {\n return fail(\"NETWORK\", \"default branch lookup returned no default_branch\");\n }\n return data.default_branch;\n}\n\nfunction analyzeEntries(entries: TarEntryInfo[], requestedPath: string): {\n prefix: string;\n execSet: Set<string>;\n} {\n if (entries.length === 0) {\n return fail(\"NOT_FOUND\", \"archive contained no entries\");\n }\n\n const prefix = entries[0].path.split(\"/\")[0];\n if (!prefix) {\n return fail(\"IO\", \"unable to determine archive prefix\");\n }\n\n const execSet = new Set<string>();\n let entryCount = 0;\n let totalSize = 0;\n let matched = false;\n\n for (const entry of entries) {\n assertSafeTarPath(entry.path);\n if (!ALLOWED_TYPES.has(entry.type)) {\n return fail(\"IO\", `unsupported entry type in archive: ${entry.path}`);\n }\n\n if (entry.path === prefix) {\n continue;\n }\n if (!entry.path.startsWith(`${prefix}/`)) {\n return fail(\"IO\", \"archive has unexpected top-level layout\");\n }\n\n const rel = entry.path.slice(prefix.length + 1);\n if (!rel) {\n continue;\n }\n\n const relToReq = stripRequestedPath(rel, requestedPath);\n if (relToReq === null) {\n continue;\n }\n\n matched = true;\n entryCount += 1;\n if (rel === requestedPath && entry.type === \"File\") {\n return fail(\"INVALID_PATH\", \"requested path is not a directory\");\n }\n if (entry.type === \"File\") {\n totalSize += entry.size ?? 0;\n if (entry.mode & 0o111) {\n execSet.add(relToReq);\n }\n }\n }\n\n if (!matched) {\n return fail(\"NOT_FOUND\", \"requested path not found in archive\");\n }\n if (entryCount > MAX_SKILLREF_FILES) {\n return fail(\"SIZE_LIMIT\", `extracted entry count ${entryCount} exceeds ${MAX_SKILLREF_FILES}`);\n }\n if (totalSize > MAX_SKILLREF_EXTRACT_BYTES) {\n return fail(\"SIZE_LIMIT\", `extracted size ${totalSize} exceeds ${MAX_SKILLREF_EXTRACT_BYTES}`);\n }\n\n return { prefix, execSet };\n}\n\nfunction stripRequestedPath(rel: string, requestedPath: string): string | null {\n if (rel === requestedPath) {\n return \"\";\n }\n const prefix = requestedPath + \"/\";\n if (rel.startsWith(prefix)) {\n return rel.slice(prefix.length);\n }\n return null;\n}\n\nfunction isUnderPath(entryPath: string, prefix: string, requestedPath: string): boolean {\n if (entryPath === prefix) {\n return false;\n }\n if (!entryPath.startsWith(`${prefix}/`)) {\n return false;\n }\n const rel = entryPath.slice(prefix.length + 1);\n if (!rel) {\n return false;\n }\n if (rel === requestedPath || rel.startsWith(requestedPath + \"/\")) {\n return true;\n }\n return false;\n}\n","import { SKILLPACK_HEADER, SKILLREF_HEADER } from \"./constants\";\n\nexport type SkillValueType = \"skillpack\" | \"skillref\" | \"string\";\n\nexport function detectSkillValueType(value: string): SkillValueType {\n if (value.startsWith(SKILLPACK_HEADER)) return \"skillpack\";\n if (value.startsWith(SKILLREF_HEADER)) return \"skillref\";\n return \"string\";\n}\n","import fs from \"node:fs/promises\";\nimport process from \"node:process\";\nimport { fail } from \"./errors\";\nimport { createSkillpackFromDir } from \"./skillpack\";\nimport { createSkillrefValue } from \"./skillref\";\n\nexport type SaveInput = { kind: \"string\" | \"skillpack\" | \"skillref\"; value: string };\n\nexport interface SaveOptions {\n append: boolean;\n file?: string;\n value?: string;\n dir?: string;\n url?: string;\n ref?: string;\n path?: string;\n}\n\nexport async function resolveSaveInput(\n resource: string,\n opts: SaveOptions,\n stdinIsTTY: boolean = Boolean(process.stdin.isTTY)\n): Promise<SaveInput> {\n const hasFile = typeof opts.file === \"string\";\n const hasValue = typeof opts.value === \"string\";\n const hasDir = typeof opts.dir === \"string\";\n const urlFlagsUsed = Boolean(opts.url || opts.ref || opts.path);\n const hasUrl = Boolean(opts.url && opts.path);\n const explicitCount = [hasFile, hasValue, hasDir, hasUrl].filter(Boolean).length;\n const hasStdin = !stdinIsTTY && explicitCount === 0;\n\n if (urlFlagsUsed && !hasUrl) {\n return fail(\"INVALID_INPUT\", \"--url and --path must be provided together\");\n }\n\n const methods = explicitCount + (hasStdin ? 1 : 0);\n if (methods !== 1) {\n return fail(\"INVALID_INPUT\", \"exactly one input method must be used\");\n }\n\n if (hasDir && resource !== \"skill\") {\n return fail(\"INVALID_INPUT\", \"--dir is only valid for skill save\");\n }\n if (hasUrl && resource !== \"skill\") {\n return fail(\"INVALID_INPUT\", \"--url/--ref/--path are only valid for skill save\");\n }\n if (opts.append && (hasDir || hasUrl)) {\n return fail(\"INVALID_INPUT\", \"--append cannot be used with --dir or --url\");\n }\n\n if (hasDir) {\n const value = await createSkillpackFromDir(opts.dir as string);\n return { kind: \"skillpack\", value };\n }\n\n if (hasUrl) {\n const value = createSkillrefValue(opts.url as string, opts.path as string, opts.ref as string | undefined);\n return { kind: \"skillref\", value };\n }\n\n if (hasFile) {\n const content = await fs.readFile(opts.file as string, \"utf8\");\n return { kind: \"string\", value: content };\n }\n\n if (hasValue) {\n return { kind: \"string\", value: opts.value as string };\n }\n\n const stdin = await readStdin();\n return { kind: \"string\", value: stdin };\n}\n\nasync function readStdin(): Promise<string> {\n return new Promise((resolve, reject) => {\n let data = \"\";\n process.stdin.setEncoding(\"utf8\");\n process.stdin.on(\"data\", (chunk) => {\n data += chunk;\n });\n process.stdin.on(\"end\", () => resolve(data));\n process.stdin.on(\"error\", reject);\n });\n}\n"],"mappings":"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","names":["import_node_util","import_node_process","import_node_path","import_promises","import_node_fs","path","os","fs","import_node_path","path","import_promises","import_node_path","import_node_os","import_tar","import_promises","import_node_path","import_promises","path","fs","fs","path","import_promises","import_node_path","fs","path","import_node_path","path","tar","fs","path","os","tar","zlib","import_promises","import_node_path","import_node_os","import_node_fs","import_tar","ALLOWED_TYPES","fs","path","os","tar","import_promises","process","fs","process","path","fs"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../src/errors.ts","../src/config.ts","../src/constants.ts","../src/store.ts","../src/git.ts","../src/skillpack.ts","../src/fs-ops.ts","../src/chmod.ts","../src/perm.ts","../src/validators.ts","../src/tar-utils.ts","../src/skillref.ts","../src/value.ts","../src/input.ts"],"sourcesContent":["import { parseArgs } from \"node:util\";\nimport process from \"node:process\";\nimport path from \"node:path\";\nimport fs from \"node:fs/promises\";\nimport { readFileSync } from \"node:fs\";\nimport { formatError, fail } from \"./errors\";\nimport { loadConfig, writeConfig } from \"./config\";\nimport { createStore } from \"./store\";\nimport { inferCtxKey } from \"./git\";\nimport { extractSkillpackToDir } from \"./skillpack\";\nimport { loadSkillrefToDir } from \"./skillref\";\nimport { detectSkillValueType } from \"./value\";\nimport { resolveSaveInput } from \"./input\";\n\nasync function main(): Promise<void> {\n let positionals: string[];\n let values: Record<string, unknown>;\n try {\n ({ positionals, values } = parseArgs({\n args: process.argv.slice(2),\n options: {\n append: { type: \"boolean\" },\n version: { type: \"boolean\", short: \"v\" },\n file: { type: \"string\" },\n value: { type: \"string\" },\n dir: { type: \"string\" },\n url: { type: \"string\" },\n ref: { type: \"string\" },\n path: { type: \"string\" },\n },\n allowPositionals: true,\n }));\n } catch (err) {\n return fail(\"INVALID_INPUT\", err instanceof Error ? err.message : \"invalid arguments\");\n }\n\n const [resource, command, keyArg, ...extra] = positionals;\n if (values.version) {\n process.stdout.write(getVersion() + \"\\n\");\n return;\n }\n if (!resource) {\n return fail(\"INVALID_INPUT\", \"missing resource\");\n }\n\n if (resource === \"init\") {\n if (command || keyArg || extra.length) {\n return fail(\"INVALID_INPUT\", \"init does not accept additional arguments\");\n }\n await runInit();\n return;\n }\n\n if (!command) {\n return fail(\"INVALID_INPUT\", \"missing command\");\n }\n if (extra.length > 0) {\n return fail(\"INVALID_INPUT\", \"too many positional arguments\");\n }\n\n const opts = {\n append: Boolean(values.append),\n file: values.file as string | undefined,\n value: values.value as string | undefined,\n dir: values.dir as string | undefined,\n url: values.url as string | undefined,\n ref: values.ref as string | undefined,\n path: values.path as string | undefined,\n };\n\n let store: ReturnType<typeof createStore>;\n try {\n const storeConfig = await loadConfig();\n store = createStore(storeConfig.url, storeConfig.token);\n } catch (err) {\n if (resource === \"skill\" && command === \"load\" && keyArg === \"ctxbin\") {\n const fallback = await loadBundledSkill();\n if (fallback) {\n process.stdout.write(fallback);\n return;\n }\n }\n throw err;\n }\n\n const hash = resolveHash(resource);\n\n if (command === \"list\") {\n if (keyArg) {\n return fail(\"INVALID_INPUT\", \"list does not accept a key\");\n }\n ensureNoListFlags(opts);\n await handleList(store, resource, hash);\n return;\n }\n\n const key = await resolveKey(resource, keyArg);\n\n switch (command) {\n case \"load\":\n await handleLoad(store, resource, hash, key, opts);\n return;\n case \"save\":\n await handleSave(store, resource, hash, key, opts);\n return;\n case \"delete\":\n await handleDelete(store, resource, hash, key, opts);\n return;\n default:\n return fail(\"INVALID_INPUT\", `unknown command: ${command}`);\n }\n}\n\nasync function runInit(): Promise<void> {\n const readline = await import(\"node:readline/promises\");\n const rl = readline.createInterface({ input: process.stdin, output: process.stdout });\n const url = (await rl.question(\"CTXBIN_STORE_URL: \")).trim();\n const token = (await rl.question(\"CTXBIN_STORE_TOKEN: \")).trim();\n await rl.close();\n\n if (!url || !token) {\n return fail(\"INVALID_INPUT\", \"both URL and token are required\");\n }\n\n await writeConfig({ url, token });\n}\n\nfunction resolveHash(resource: string): \"ctx\" | \"agent\" | \"skill\" {\n if (resource === \"ctx\" || resource === \"agent\" || resource === \"skill\") {\n return resource;\n }\n return fail(\"INVALID_INPUT\", `unknown resource: ${resource}`);\n}\n\nasync function resolveKey(resource: string, keyArg?: string): Promise<string> {\n if (resource === \"ctx\") {\n if (keyArg) return keyArg;\n return inferCtxKey();\n }\n if (!keyArg) {\n return fail(\"MISSING_KEY\", \"key is required\");\n }\n return keyArg;\n}\n\nasync function handleLoad(\n store: ReturnType<typeof createStore>,\n resource: string,\n hash: string,\n key: string,\n opts: {\n append: boolean;\n file?: string;\n value?: string;\n dir?: string;\n url?: string;\n ref?: string;\n path?: string;\n }\n): Promise<void> {\n ensureNoSaveInput(opts, \"load\");\n\n const value = await store.get(hash, key);\n if (value === null) {\n if (resource === \"skill\" && key === \"ctxbin\") {\n const fallback = await loadBundledSkill();\n if (fallback) {\n process.stdout.write(fallback);\n return;\n }\n }\n return fail(\"NOT_FOUND\", `no value for ${hash}:${key}`);\n }\n\n if (resource === \"skill\") {\n const kind = detectSkillValueType(value);\n if (kind === \"string\") {\n if (opts.dir) {\n return fail(\"TYPE_MISMATCH\", \"--dir cannot be used with string values\");\n }\n process.stdout.write(value);\n return;\n }\n if (!opts.dir) {\n return fail(\"TYPE_MISMATCH\", \"--dir is required for skillpack/skillref load\");\n }\n if (kind === \"skillpack\") {\n await extractSkillpackToDir(value, opts.dir);\n return;\n }\n if (kind === \"skillref\") {\n await loadSkillrefToDir(value, opts.dir);\n return;\n }\n }\n\n if (opts.dir) {\n return fail(\"TYPE_MISMATCH\", \"--dir is only valid for skill values\");\n }\n process.stdout.write(value);\n}\n\nasync function handleSave(\n store: ReturnType<typeof createStore>,\n resource: string,\n hash: string,\n key: string,\n opts: {\n append: boolean;\n file?: string;\n value?: string;\n dir?: string;\n url?: string;\n ref?: string;\n path?: string;\n }\n): Promise<void> {\n const input = await resolveSaveInput(resource, opts);\n\n if (opts.append) {\n if (input.kind !== \"string\") {\n return fail(\"INVALID_INPUT\", \"--append only applies to string inputs\");\n }\n const existing = await store.get(hash, key);\n if (resource === \"skill\" && existing && detectSkillValueType(existing) !== \"string\") {\n return fail(\"TYPE_MISMATCH\", \"cannot append to skillpack/skillref values\");\n }\n const merged = existing ? `${existing}\\n\\n${input.value}` : input.value;\n await store.set(hash, key, merged);\n return;\n }\n\n if (input.kind !== \"string\" && resource !== \"skill\") {\n return fail(\"TYPE_MISMATCH\", \"non-string inputs are only valid for skill\" );\n }\n\n await store.set(hash, key, input.value);\n}\n\nasync function handleDelete(\n store: ReturnType<typeof createStore>,\n resource: string,\n hash: string,\n key: string,\n opts: {\n append: boolean;\n file?: string;\n value?: string;\n dir?: string;\n url?: string;\n ref?: string;\n path?: string;\n }\n): Promise<void> {\n ensureNoSaveInput(opts, \"delete\");\n if (opts.dir) {\n return fail(\"INVALID_INPUT\", \"--dir is not valid for delete\");\n }\n await store.delete(hash, key);\n}\n\nasync function handleList(\n store: ReturnType<typeof createStore>,\n resource: string,\n hash: string\n): Promise<void> {\n const entries = await store.list(hash);\n if (entries.length === 0) return;\n\n const lines = entries.map(({ field, value }) => {\n let type = \"--value\";\n if (resource === \"skill\") {\n const kind = detectSkillValueType(value);\n if (kind === \"skillpack\") type = \"--dir\";\n if (kind === \"skillref\") type = \"--url\";\n }\n return `${field}\\t${type}`;\n });\n\n process.stdout.write(lines.join(\"\\n\"));\n}\n\n\nfunction ensureNoSaveInput(\n opts: {\n append: boolean;\n file?: string;\n value?: string;\n dir?: string;\n url?: string;\n ref?: string;\n path?: string;\n },\n command: \"load\" | \"delete\"\n): void {\n if (opts.append || opts.file || opts.value || opts.url || opts.ref || opts.path) {\n return fail(\"INVALID_INPUT\", `${command} does not accept input flags`);\n }\n}\n\nfunction ensureNoListFlags(opts: {\n append: boolean;\n file?: string;\n value?: string;\n dir?: string;\n url?: string;\n ref?: string;\n path?: string;\n}): void {\n if (opts.append || opts.file || opts.value || opts.dir || opts.url || opts.ref || opts.path) {\n return fail(\"INVALID_INPUT\", \"list does not accept input flags\");\n }\n}\n\nmain().catch((err) => {\n process.stderr.write(formatError(err) + \"\\n\");\n process.exit(1);\n});\n\nasync function loadBundledSkill(): Promise<string | null> {\n try {\n const bundled = path.resolve(__dirname, \"skills\", \"ctxbin\", \"SKILL.md\");\n return await fs.readFile(bundled, \"utf8\");\n } catch {\n return null;\n }\n}\n\nfunction getVersion(): string {\n try {\n const pkgPath = path.resolve(__dirname, \"..\", \"package.json\");\n const raw = readFileSync(pkgPath, \"utf8\");\n const data = JSON.parse(raw);\n if (data && typeof data.version === \"string\") {\n return data.version;\n }\n } catch {\n // ignore\n }\n return \"0.0.0\";\n}\n","export type ErrorCode =\n | \"INVALID_INPUT\"\n | \"MISSING_KEY\"\n | \"INVALID_URL\"\n | \"INVALID_REF\"\n | \"INVALID_PATH\"\n | \"NOT_IN_GIT\"\n | \"NOT_FOUND\"\n | \"TYPE_MISMATCH\"\n | \"SIZE_LIMIT\"\n | \"NETWORK\"\n | \"IO\";\n\nexport class CtxbinError extends Error {\n readonly code: ErrorCode;\n\n constructor(code: ErrorCode, message: string) {\n super(message);\n this.code = code;\n }\n}\n\nexport function fail(code: ErrorCode, message: string): never {\n throw new CtxbinError(code, message);\n}\n\nexport function formatError(err: unknown): string {\n if (err instanceof CtxbinError) {\n return `CTXBIN_ERR ${err.code}: ${err.message}`;\n }\n const message = err instanceof Error ? err.message : String(err);\n return `CTXBIN_ERR IO: ${message}`;\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport os from \"node:os\";\nimport { fail } from \"./errors\";\n\nexport interface StoreConfig {\n url: string;\n token: string;\n}\n\nconst ENV_URL = \"CTXBIN_STORE_URL\";\nconst ENV_TOKEN = \"CTXBIN_STORE_TOKEN\";\n\nexport async function loadConfig(): Promise<StoreConfig> {\n const envUrl = process.env[ENV_URL];\n const envToken = process.env[ENV_TOKEN];\n\n if (envUrl || envToken) {\n if (!envUrl || !envToken) {\n return fail(\"INVALID_INPUT\", \"both CTXBIN_STORE_URL and CTXBIN_STORE_TOKEN must be set\");\n }\n return { url: envUrl, token: envToken };\n }\n\n const configPath = path.join(os.homedir(), \".ctxbin\", \"config.json\");\n let raw: string;\n try {\n raw = await fs.readFile(configPath, \"utf8\");\n } catch {\n return fail(\"INVALID_INPUT\", \"missing CTXBIN_STORE_URL/CTXBIN_STORE_TOKEN and no ~/.ctxbin/config.json\");\n }\n\n let parsed: any;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return fail(\"INVALID_INPUT\", \"invalid JSON in ~/.ctxbin/config.json\");\n }\n\n const url = parsed.store_url || parsed.storeUrl || parsed.url;\n const token = parsed.store_token || parsed.storeToken || parsed.token;\n if (!url || !token) {\n return fail(\"INVALID_INPUT\", \"config.json must include store_url and store_token\");\n }\n\n return { url, token };\n}\n\nexport async function writeConfig(config: StoreConfig): Promise<void> {\n const dir = path.join(os.homedir(), \".ctxbin\");\n const configPath = path.join(dir, \"config.json\");\n await fs.mkdir(dir, { recursive: true, mode: 0o700 });\n const payload = {\n store_url: config.url,\n store_token: config.token,\n };\n await fs.writeFile(configPath, JSON.stringify(payload, null, 2), \"utf8\");\n}\n","export const SKILLPACK_HEADER = \"ctxbin-skillpack@1\\n\";\nexport const SKILLREF_HEADER = \"ctxbin-skillref@1\\n\";\n\nexport const MAX_SKILLPACK_BYTES = 7 * 1024 * 1024;\nexport const MAX_SKILLREF_DOWNLOAD_BYTES = 20 * 1024 * 1024;\nexport const MAX_SKILLREF_EXTRACT_BYTES = 100 * 1024 * 1024;\nexport const MAX_SKILLREF_FILES = 5000;\n\nexport const SKILLREF_CONNECT_TIMEOUT_MS = 5000;\nexport const SKILLREF_DOWNLOAD_TIMEOUT_MS = 30000;\n\nexport const DEFAULT_EXCLUDES = [\".git\", \"node_modules\", \".DS_Store\"];\n\nexport const STORE_REQUEST_TIMEOUT_MS = 5000;\n","import { fail } from \"./errors\";\nimport { STORE_REQUEST_TIMEOUT_MS } from \"./constants\";\n\nexport interface Store {\n get(hash: string, field: string): Promise<string | null>;\n set(hash: string, field: string, value: string): Promise<void>;\n delete(hash: string, field: string): Promise<void>;\n list(hash: string): Promise<Array<{ field: string; value: string }>>;\n}\n\ninterface UpstashResponse<T> {\n result?: T;\n error?: string;\n}\n\nexport function createStore(url: string, token: string): Store {\n const baseUrl = url.replace(/\\/$/, \"\");\n\n async function command<T>(cmd: string, ...args: string[]): Promise<T> {\n let res: Response;\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), STORE_REQUEST_TIMEOUT_MS);\n try {\n res = await fetch(baseUrl, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify([cmd, ...args]),\n signal: controller.signal,\n });\n } catch (err) {\n if (controller.signal.aborted) {\n return fail(\"NETWORK\", `store request timed out after ${STORE_REQUEST_TIMEOUT_MS}ms`);\n }\n return fail(\"NETWORK\", `store request failed: ${err instanceof Error ? err.message : String(err)}`);\n } finally {\n clearTimeout(timeout);\n }\n\n if (!res.ok) {\n const text = await res.text();\n return fail(\"NETWORK\", `store request failed (${res.status}): ${text}`);\n }\n\n let data: UpstashResponse<T>;\n try {\n data = (await res.json()) as UpstashResponse<T>;\n } catch {\n return fail(\"NETWORK\", \"store response was not valid JSON\");\n }\n\n if (data.error) {\n return fail(\"NETWORK\", data.error);\n }\n\n return data.result as T;\n }\n\n return {\n async get(hash, field) {\n const result = await command<string | null>(\"HGET\", hash, field);\n return result ?? null;\n },\n async set(hash, field, value) {\n await command<number>(\"HSET\", hash, field, value);\n },\n async delete(hash, field) {\n await command<number>(\"HDEL\", hash, field);\n },\n async list(hash) {\n const result = await command<unknown>(\"HGETALL\", hash);\n if (!result) return [];\n if (!Array.isArray(result)) {\n return fail(\"NETWORK\", \"store response for HGETALL was not an array\");\n }\n const pairs: Array<{ field: string; value: string }> = [];\n for (let i = 0; i < result.length; i += 2) {\n const field = result[i];\n const value = result[i + 1];\n if (typeof field !== \"string\" || typeof value !== \"string\") {\n return fail(\"NETWORK\", \"store response for HGETALL contained invalid entries\");\n }\n pairs.push({ field, value });\n }\n pairs.sort((a, b) => a.field.localeCompare(b.field));\n return pairs;\n },\n };\n}\n","import { execFile } from \"node:child_process\";\nimport path from \"node:path\";\nimport { promisify } from \"node:util\";\nimport { fail } from \"./errors\";\n\nconst execFileAsync = promisify(execFile);\n\nasync function git(args: string[]): Promise<string> {\n try {\n const { stdout } = await execFileAsync(\"git\", args, { encoding: \"utf8\" });\n return stdout.trim();\n } catch {\n return fail(\"NOT_IN_GIT\", \"not inside a git repository\");\n }\n}\n\nexport async function inferCtxKey(): Promise<string> {\n const root = await git([\"rev-parse\", \"--show-toplevel\"]);\n const branch = await git([\"rev-parse\", \"--abbrev-ref\", \"HEAD\"]);\n const project = path.basename(root);\n if (!project || !branch) {\n return fail(\"NOT_IN_GIT\", \"unable to infer ctx key from git repository\");\n }\n return `${project}/${branch}`;\n}\n","import fs from \"node:fs/promises\";\nimport { createWriteStream } from \"node:fs\";\nimport path from \"node:path\";\nimport os from \"node:os\";\nimport zlib from \"node:zlib\";\nimport { pipeline } from \"node:stream/promises\";\nimport tar from \"tar\";\nimport { DEFAULT_EXCLUDES, MAX_SKILLPACK_BYTES, SKILLPACK_HEADER } from \"./constants\";\nimport { fail } from \"./errors\";\nimport { ensureDir, copyDirContents } from \"./fs-ops\";\nimport { applyNormalizedPermissions } from \"./perm\";\nimport { assertSafeTarPath } from \"./validators\";\nimport { listTarEntries } from \"./tar-utils\";\n\nconst ALLOWED_TYPES = new Set([\"File\", \"Directory\"]);\n\nexport async function createSkillpackFromDir(dirPath: string): Promise<string> {\n const stats = await fs.stat(dirPath).catch(() => null);\n if (!stats || !stats.isDirectory()) {\n return fail(\"INVALID_INPUT\", `--dir is not a directory: ${dirPath}`);\n }\n\n const entries = await collectEntries(dirPath);\n const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), \"ctxbin-skillpack-\"));\n const tarPath = path.join(tmpDir, \"skillpack.tar.gz\");\n\n try {\n const tarStream = tar.c(\n {\n cwd: dirPath,\n portable: true,\n mtime: new Date(0),\n },\n entries\n );\n\n const gzip = zlib.createGzip({ mtime: 0 });\n await pipeline(tarStream, gzip, createWriteStream(tarPath));\n\n const stat = await fs.stat(tarPath);\n if (stat.size > MAX_SKILLPACK_BYTES) {\n return fail(\n \"SIZE_LIMIT\",\n `skillpack tar.gz size ${stat.size} bytes exceeds ${MAX_SKILLPACK_BYTES} bytes`\n );\n }\n\n const data = await fs.readFile(tarPath);\n const b64 = data.toString(\"base64\");\n return SKILLPACK_HEADER + b64;\n } finally {\n await fs.rm(tmpDir, { recursive: true, force: true });\n }\n}\n\nexport async function extractSkillpackToDir(value: string, targetDir: string): Promise<void> {\n const base64 = value.slice(SKILLPACK_HEADER.length);\n let buffer: Buffer;\n try {\n buffer = Buffer.from(base64, \"base64\");\n } catch {\n return fail(\"IO\", \"invalid skillpack base64 data\");\n }\n\n const tmpRoot = await fs.mkdtemp(path.join(os.tmpdir(), \"ctxbin-skillpack-\"));\n const tarPath = path.join(tmpRoot, \"skillpack.tar.gz\");\n await fs.writeFile(tarPath, buffer);\n\n try {\n const entries = await listTarEntries(tarPath);\n const execSet = validateTarEntries(entries);\n\n const extractDir = path.join(tmpRoot, \"extract\");\n await ensureDir(extractDir);\n await tar.x({\n file: tarPath,\n cwd: extractDir,\n preserveOwner: false,\n noMtime: true,\n });\n\n await applyNormalizedPermissions(extractDir, execSet);\n await ensureDir(targetDir);\n await copyDirContents(extractDir, targetDir);\n } finally {\n await fs.rm(tmpRoot, { recursive: true, force: true });\n }\n}\n\nasync function collectEntries(root: string): Promise<string[]> {\n const results: string[] = [];\n\n async function walk(absDir: string, relDir: string): Promise<void> {\n const entries = await fs.readdir(absDir, { withFileTypes: true });\n entries.sort((a, b) => a.name.localeCompare(b.name));\n\n for (const entry of entries) {\n if (DEFAULT_EXCLUDES.includes(entry.name)) {\n if (entry.isDirectory()) {\n continue;\n }\n if (entry.isFile() && entry.name === \".DS_Store\") {\n continue;\n }\n }\n\n const absPath = path.join(absDir, entry.name);\n const relPath = relDir ? path.posix.join(relDir, entry.name) : entry.name;\n const stat = await fs.lstat(absPath);\n if (stat.isSymbolicLink()) {\n return fail(\"IO\", `symlink not allowed in skillpack: ${absPath}`);\n }\n\n if (entry.isDirectory()) {\n results.push(relPath);\n await walk(absPath, relPath);\n continue;\n }\n\n if (entry.isFile()) {\n if (entry.name === \".DS_Store\") {\n continue;\n }\n results.push(relPath);\n continue;\n }\n\n return fail(\"IO\", `unsupported file type in skillpack: ${absPath}`);\n }\n }\n\n await walk(root, \"\");\n results.sort();\n return results;\n}\n\nfunction validateTarEntries(entries: { path: string; type: string; mode: number }[]): Set<string> {\n const execSet = new Set<string>();\n for (const entry of entries) {\n assertSafeTarPath(entry.path);\n if (!ALLOWED_TYPES.has(entry.type)) {\n return fail(\"IO\", `unsupported entry type in skillpack: ${entry.path}`);\n }\n if (entry.type === \"File\" && (entry.mode & 0o111)) {\n execSet.add(entry.path);\n }\n }\n return execSet;\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { fail } from \"./errors\";\nimport { safeChmod } from \"./chmod\";\n\nexport async function ensureDir(dir: string): Promise<void> {\n await fs.mkdir(dir, { recursive: true });\n}\n\nexport function toPosix(p: string): string {\n return p.split(path.sep).join(\"/\");\n}\n\nexport async function copyDirContents(src: string, dest: string): Promise<void> {\n await ensureDir(dest);\n const entries = await fs.readdir(src, { withFileTypes: true });\n for (const entry of entries) {\n const srcPath = path.join(src, entry.name);\n const destPath = path.join(dest, entry.name);\n if (entry.isDirectory()) {\n await copyDirContents(srcPath, destPath);\n const stat = await fs.stat(srcPath);\n await safeChmod(destPath, stat.mode & 0o777);\n continue;\n }\n if (entry.isFile()) {\n await ensureDir(path.dirname(destPath));\n await fs.copyFile(srcPath, destPath);\n const stat = await fs.stat(srcPath);\n await safeChmod(destPath, stat.mode & 0o777);\n continue;\n }\n return fail(\"IO\", `unsupported file type during copy: ${srcPath}`);\n }\n}\n","import fs from \"node:fs/promises\";\n\nexport async function safeChmod(path: string, mode: number): Promise<void> {\n try {\n await fs.chmod(path, mode);\n } catch (err) {\n if (process.platform === \"win32\") {\n return;\n }\n throw err;\n }\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { fail } from \"./errors\";\nimport { toPosix } from \"./fs-ops\";\nimport { safeChmod } from \"./chmod\";\n\nexport async function applyNormalizedPermissions(root: string, execSet: Set<string>): Promise<void> {\n async function walk(absDir: string): Promise<void> {\n const entries = await fs.readdir(absDir, { withFileTypes: true });\n for (const entry of entries) {\n const absPath = path.join(absDir, entry.name);\n if (entry.isDirectory()) {\n await safeChmod(absPath, 0o755);\n await walk(absPath);\n continue;\n }\n if (entry.isFile()) {\n const rel = toPosix(path.relative(root, absPath));\n const mode = execSet.has(rel) ? 0o755 : 0o644;\n await safeChmod(absPath, mode);\n continue;\n }\n return fail(\"IO\", `unsupported file type after extract: ${absPath}`);\n }\n }\n\n await walk(root);\n}\n","import path from \"node:path\";\nimport { fail } from \"./errors\";\n\nexport function normalizeGithubUrl(input: string): string {\n let url: URL;\n try {\n url = new URL(input);\n } catch {\n return fail(\"INVALID_URL\", \"invalid URL\");\n }\n\n if (url.protocol !== \"https:\") {\n return fail(\"INVALID_URL\", \"URL must use https\");\n }\n if (url.hostname !== \"github.com\") {\n return fail(\"INVALID_URL\", \"only github.com is supported\");\n }\n if (url.search || url.hash) {\n return fail(\"INVALID_URL\", \"URL must not include query or hash\");\n }\n\n const parts = url.pathname.split(\"/\").filter(Boolean);\n if (parts.length !== 2) {\n return fail(\"INVALID_URL\", \"URL must be https://github.com/<owner>/<repo>\");\n }\n const owner = parts[0];\n let repo = parts[1];\n if (repo.endsWith(\".git\")) {\n repo = repo.slice(0, -4);\n }\n if (!owner || !repo) {\n return fail(\"INVALID_URL\", \"URL must be https://github.com/<owner>/<repo>\");\n }\n\n return `https://github.com/${owner}/${repo}`;\n}\n\nexport function validateCommitSha(ref: string): string {\n if (!/^[0-9a-f]{40}$/.test(ref)) {\n return fail(\"INVALID_REF\", \"ref must be a 40-hex commit SHA\");\n }\n return ref;\n}\n\nexport function normalizeSkillPath(input: string): string {\n const trimmed = input.trim();\n if (!trimmed) {\n return fail(\"INVALID_PATH\", \"path must be a non-empty directory path\");\n }\n const cleaned = trimmed.replace(/\\\\/g, \"/\");\n if (cleaned.startsWith(\"/\")) {\n return fail(\"INVALID_PATH\", \"path must be relative, not absolute\");\n }\n const normalized = path.posix.normalize(cleaned).replace(/^\\.\\//, \"\");\n if (normalized === \".\" || normalized === \"\") {\n return fail(\"INVALID_PATH\", \"path must be a non-empty directory path\");\n }\n if (normalized.startsWith(\"../\") || normalized.includes(\"/../\") || normalized === \"..\") {\n return fail(\"INVALID_PATH\", \"path must not include .. segments\");\n }\n if (normalized.endsWith(\"/\")) {\n return normalized.slice(0, -1);\n }\n return normalized;\n}\n\nexport function assertSafeTarPath(entryPath: string): void {\n const cleaned = entryPath.replace(/\\\\/g, \"/\");\n if (cleaned.startsWith(\"/\")) {\n return fail(\"INVALID_PATH\", `tar entry path must be relative: ${entryPath}`);\n }\n const normalized = path.posix.normalize(cleaned);\n if (normalized.startsWith(\"../\") || normalized === \"..\" || normalized.includes(\"/../\")) {\n return fail(\"INVALID_PATH\", `tar entry path contains traversal: ${entryPath}`);\n }\n}\n","import tar from \"tar\";\n\nexport interface TarEntryInfo {\n path: string;\n type: string;\n size: number;\n mode: number;\n}\n\nexport async function listTarEntries(file: string): Promise<TarEntryInfo[]> {\n const entries: TarEntryInfo[] = [];\n await tar.t({\n file,\n onentry(entry) {\n entries.push({\n path: entry.path,\n type: entry.type,\n size: entry.size ?? 0,\n mode: entry.mode ?? 0,\n });\n },\n });\n return entries;\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport os from \"node:os\";\nimport { createWriteStream } from \"node:fs\";\nimport { SKILLREF_HEADER, MAX_SKILLREF_DOWNLOAD_BYTES, MAX_SKILLREF_EXTRACT_BYTES, MAX_SKILLREF_FILES, SKILLREF_CONNECT_TIMEOUT_MS, SKILLREF_DOWNLOAD_TIMEOUT_MS } from \"./constants\";\nimport { fail, CtxbinError } from \"./errors\";\nimport { normalizeGithubUrl, normalizeSkillPath, validateCommitSha, assertSafeTarPath } from \"./validators\";\nimport { listTarEntries, TarEntryInfo } from \"./tar-utils\";\nimport tar from \"tar\";\nimport { ensureDir, copyDirContents } from \"./fs-ops\";\nimport { applyNormalizedPermissions } from \"./perm\";\n\nconst ALLOWED_TYPES = new Set([\"File\", \"Directory\"]);\n\nexport interface Skillref {\n url: string;\n path: string;\n ref?: string;\n track?: \"default\";\n}\n\nexport function createSkillrefValue(url: string, skillPath: string, ref?: string): string {\n const normalizedUrl = normalizeGithubUrl(url);\n const normalizedPath = normalizeSkillPath(skillPath);\n const payload = ref\n ? JSON.stringify({ url: normalizedUrl, path: normalizedPath, ref: validateCommitSha(ref) })\n : JSON.stringify({ url: normalizedUrl, path: normalizedPath, track: \"default\" });\n return SKILLREF_HEADER + payload;\n}\n\nexport function parseSkillrefValue(value: string): Skillref {\n if (!value.startsWith(SKILLREF_HEADER)) {\n return fail(\"TYPE_MISMATCH\", \"value is not a skillref\");\n }\n const raw = value.slice(SKILLREF_HEADER.length);\n let parsed: any;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return fail(\"IO\", \"invalid skillref payload JSON\");\n }\n if (!parsed || typeof parsed.url !== \"string\" || typeof parsed.path !== \"string\") {\n return fail(\"IO\", \"invalid skillref payload fields\");\n }\n const normalized = {\n url: normalizeGithubUrl(parsed.url),\n path: normalizeSkillPath(parsed.path),\n } satisfies Pick<Skillref, \"url\" | \"path\">;\n\n if (typeof parsed.ref === \"string\") {\n return { ...normalized, ref: validateCommitSha(parsed.ref) };\n }\n\n if (parsed.track === \"default\") {\n return { ...normalized, track: \"default\" };\n }\n\n return fail(\"IO\", \"invalid skillref payload fields\");\n}\n\nexport async function loadSkillrefToDir(value: string, targetDir: string): Promise<void> {\n const skillref = parseSkillrefValue(value);\n const resolvedRef = skillref.ref ?? (await fetchDefaultBranch(skillref.url));\n const tmpRoot = await fs.mkdtemp(path.join(os.tmpdir(), \"ctxbin-skillref-\"));\n const tarPath = path.join(tmpRoot, \"skillref.tar.gz\");\n\n try {\n await downloadArchive(skillref.url, resolvedRef, tarPath);\n\n const entries = await listTarEntries(tarPath).catch(() => fail(\"IO\", \"failed to parse tar archive\"));\n const analysis = analyzeEntries(entries, skillref.path);\n\n const extractDir = path.join(tmpRoot, \"extract\");\n await ensureDir(extractDir);\n\n const stripCount = 1 + skillref.path.split(\"/\").length;\n await tar.x({\n file: tarPath,\n cwd: extractDir,\n preserveOwner: false,\n noMtime: true,\n strip: stripCount,\n filter: (p, entry) => {\n const entryPath = entry?.path ?? p;\n return isUnderPath(entryPath, analysis.prefix, skillref.path);\n },\n });\n\n await applyNormalizedPermissions(extractDir, analysis.execSet);\n await ensureDir(targetDir);\n await copyDirContents(extractDir, targetDir);\n } finally {\n await fs.rm(tmpRoot, { recursive: true, force: true });\n }\n}\n\nasync function downloadArchive(repoUrl: string, ref: string, outPath: string): Promise<void> {\n const { owner, repo } = splitGithubUrl(repoUrl);\n const url = `https://codeload.github.com/${owner}/${repo}/tar.gz/${ref}`;\n const controller = new AbortController();\n const totalTimer = setTimeout(() => controller.abort(), SKILLREF_DOWNLOAD_TIMEOUT_MS);\n let res: Response;\n try {\n res = await fetchWithRedirect(url, 1, controller, [\"github.com\", \"codeload.github.com\"]);\n } catch (err) {\n clearTimeout(totalTimer);\n return fail(\"NETWORK\", `download failed: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n if (!res.ok) {\n clearTimeout(totalTimer);\n const text = await res.text();\n return fail(\"NETWORK\", `download failed (${res.status}): ${text}`);\n }\n if (!res.body) {\n clearTimeout(totalTimer);\n return fail(\"NETWORK\", \"download failed: empty response body\");\n }\n\n const fileStream = createWriteStream(outPath);\n let total = 0;\n let magic = Buffer.alloc(0);\n\n try {\n for await (const chunk of res.body as AsyncIterable<Buffer>) {\n if (magic.length < 2) {\n const needed = 2 - magic.length;\n magic = Buffer.concat([magic, chunk.subarray(0, needed)]);\n if (magic.length === 2) {\n if (magic[0] !== 0x1f || magic[1] !== 0x8b) {\n fileStream.close();\n controller.abort();\n return fail(\"IO\", \"downloaded file is not gzip data\");\n }\n }\n }\n\n total += chunk.length;\n if (total > MAX_SKILLREF_DOWNLOAD_BYTES) {\n fileStream.close();\n controller.abort();\n return fail(\n \"SIZE_LIMIT\",\n `downloaded archive size ${total} exceeds ${MAX_SKILLREF_DOWNLOAD_BYTES} bytes`\n );\n }\n\n fileStream.write(chunk);\n }\n } catch (err) {\n fileStream.close();\n clearTimeout(totalTimer);\n if (err instanceof CtxbinError) {\n throw err;\n }\n return fail(\"NETWORK\", `download failed: ${err instanceof Error ? err.message : String(err)}`);\n } finally {\n clearTimeout(totalTimer);\n }\n\n if (magic.length < 2) {\n fileStream.close();\n return fail(\"IO\", \"downloaded file is incomplete\");\n }\n\n await new Promise<void>((resolve, reject) => {\n fileStream.end(() => resolve());\n fileStream.on(\"error\", reject);\n });\n}\n\nasync function fetchWithRedirect(\n url: string,\n redirectsLeft: number,\n controller: AbortController,\n allowedHosts: string[],\n init?: RequestInit\n): Promise<Response> {\n const connectTimer = setTimeout(() => controller.abort(), SKILLREF_CONNECT_TIMEOUT_MS);\n\n const res = await fetch(url, {\n ...init,\n signal: controller.signal,\n redirect: \"manual\",\n });\n\n clearTimeout(connectTimer);\n\n if (isRedirect(res.status)) {\n if (redirectsLeft <= 0) {\n return fail(\"NETWORK\", \"too many redirects\");\n }\n const location = res.headers.get(\"location\");\n if (!location) {\n return fail(\"NETWORK\", \"redirect without location header\");\n }\n const nextUrl = new URL(location, url).toString();\n const host = new URL(nextUrl).hostname;\n if (!allowedHosts.includes(host)) {\n return fail(\"NETWORK\", `redirected to unsupported host: ${host}`);\n }\n return fetchWithRedirect(nextUrl, redirectsLeft - 1, controller, allowedHosts, init);\n }\n\n return res;\n}\n\nfunction isRedirect(status: number): boolean {\n return [301, 302, 303, 307, 308].includes(status);\n}\n\nfunction splitGithubUrl(repoUrl: string): { owner: string; repo: string } {\n const url = new URL(repoUrl);\n const parts = url.pathname.split(\"/\").filter(Boolean);\n if (parts.length !== 2) {\n return fail(\"INVALID_URL\", \"URL must be https://github.com/<owner>/<repo>\");\n }\n return { owner: parts[0], repo: parts[1] };\n}\n\nasync function fetchDefaultBranch(repoUrl: string): Promise<string> {\n const { owner, repo } = splitGithubUrl(repoUrl);\n const url = `https://api.github.com/repos/${owner}/${repo}`;\n const controller = new AbortController();\n const totalTimer = setTimeout(() => controller.abort(), SKILLREF_DOWNLOAD_TIMEOUT_MS);\n let res: Response;\n try {\n res = await fetchWithRedirect(url, 1, controller, [\"github.com\", \"api.github.com\"], {\n headers: {\n \"User-Agent\": \"ctxbin\",\n Accept: \"application/vnd.github+json\",\n },\n });\n } catch (err) {\n clearTimeout(totalTimer);\n return fail(\"NETWORK\", `default branch lookup failed: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n if (!res.ok) {\n clearTimeout(totalTimer);\n const text = await res.text();\n return fail(\"NETWORK\", `default branch lookup failed (${res.status}): ${text}`);\n }\n\n let data: any;\n try {\n data = await res.json();\n } catch {\n clearTimeout(totalTimer);\n return fail(\"NETWORK\", \"default branch lookup returned invalid JSON\");\n }\n\n clearTimeout(totalTimer);\n if (!data || typeof data.default_branch !== \"string\" || data.default_branch.length === 0) {\n return fail(\"NETWORK\", \"default branch lookup returned no default_branch\");\n }\n return data.default_branch;\n}\n\nfunction analyzeEntries(entries: TarEntryInfo[], requestedPath: string): {\n prefix: string;\n execSet: Set<string>;\n} {\n if (entries.length === 0) {\n return fail(\"NOT_FOUND\", \"archive contained no entries\");\n }\n\n const prefix = entries[0].path.split(\"/\")[0];\n if (!prefix) {\n return fail(\"IO\", \"unable to determine archive prefix\");\n }\n\n const execSet = new Set<string>();\n let entryCount = 0;\n let totalSize = 0;\n let matched = false;\n\n for (const entry of entries) {\n assertSafeTarPath(entry.path);\n if (!ALLOWED_TYPES.has(entry.type)) {\n return fail(\"IO\", `unsupported entry type in archive: ${entry.path}`);\n }\n\n if (entry.path === prefix) {\n continue;\n }\n if (!entry.path.startsWith(`${prefix}/`)) {\n return fail(\"IO\", \"archive has unexpected top-level layout\");\n }\n\n const rel = entry.path.slice(prefix.length + 1);\n if (!rel) {\n continue;\n }\n\n const relToReq = stripRequestedPath(rel, requestedPath);\n if (relToReq === null) {\n continue;\n }\n\n matched = true;\n entryCount += 1;\n if (rel === requestedPath && entry.type === \"File\") {\n return fail(\"INVALID_PATH\", \"requested path is not a directory\");\n }\n if (entry.type === \"File\") {\n totalSize += entry.size ?? 0;\n if (entry.mode & 0o111) {\n execSet.add(relToReq);\n }\n }\n }\n\n if (!matched) {\n return fail(\"NOT_FOUND\", \"requested path not found in archive\");\n }\n if (entryCount > MAX_SKILLREF_FILES) {\n return fail(\"SIZE_LIMIT\", `extracted entry count ${entryCount} exceeds ${MAX_SKILLREF_FILES}`);\n }\n if (totalSize > MAX_SKILLREF_EXTRACT_BYTES) {\n return fail(\"SIZE_LIMIT\", `extracted size ${totalSize} exceeds ${MAX_SKILLREF_EXTRACT_BYTES}`);\n }\n\n return { prefix, execSet };\n}\n\nfunction stripRequestedPath(rel: string, requestedPath: string): string | null {\n if (rel === requestedPath) {\n return \"\";\n }\n const prefix = requestedPath + \"/\";\n if (rel.startsWith(prefix)) {\n return rel.slice(prefix.length);\n }\n return null;\n}\n\nfunction isUnderPath(entryPath: string, prefix: string, requestedPath: string): boolean {\n if (entryPath === prefix) {\n return false;\n }\n if (!entryPath.startsWith(`${prefix}/`)) {\n return false;\n }\n const rel = entryPath.slice(prefix.length + 1);\n if (!rel) {\n return false;\n }\n if (rel === requestedPath || rel.startsWith(requestedPath + \"/\")) {\n return true;\n }\n return false;\n}\n","import { SKILLPACK_HEADER, SKILLREF_HEADER } from \"./constants\";\n\nexport type SkillValueType = \"skillpack\" | \"skillref\" | \"string\";\n\nexport function detectSkillValueType(value: string): SkillValueType {\n if (value.startsWith(SKILLPACK_HEADER)) return \"skillpack\";\n if (value.startsWith(SKILLREF_HEADER)) return \"skillref\";\n return \"string\";\n}\n","import fs from \"node:fs/promises\";\nimport process from \"node:process\";\nimport { fail } from \"./errors\";\nimport { createSkillpackFromDir } from \"./skillpack\";\nimport { createSkillrefValue } from \"./skillref\";\n\nexport type SaveInput = { kind: \"string\" | \"skillpack\" | \"skillref\"; value: string };\n\nexport interface SaveOptions {\n append: boolean;\n file?: string;\n value?: string;\n dir?: string;\n url?: string;\n ref?: string;\n path?: string;\n}\n\nexport async function resolveSaveInput(\n resource: string,\n opts: SaveOptions,\n stdinIsTTY: boolean = Boolean(process.stdin.isTTY)\n): Promise<SaveInput> {\n const hasFile = typeof opts.file === \"string\";\n const hasValue = typeof opts.value === \"string\";\n const hasDir = typeof opts.dir === \"string\";\n const urlFlagsUsed = Boolean(opts.url || opts.ref || opts.path);\n const hasUrl = Boolean(opts.url && opts.path);\n const explicitCount = [hasFile, hasValue, hasDir, hasUrl].filter(Boolean).length;\n const hasStdin = !stdinIsTTY && explicitCount === 0;\n\n if (urlFlagsUsed && !hasUrl) {\n return fail(\"INVALID_INPUT\", \"--url and --path must be provided together\");\n }\n\n const methods = explicitCount + (hasStdin ? 1 : 0);\n if (methods !== 1) {\n return fail(\"INVALID_INPUT\", \"exactly one input method must be used\");\n }\n\n if (hasDir && resource !== \"skill\") {\n return fail(\"INVALID_INPUT\", \"--dir is only valid for skill save\");\n }\n if (hasUrl && resource !== \"skill\") {\n return fail(\"INVALID_INPUT\", \"--url/--ref/--path are only valid for skill save\");\n }\n if (opts.append && (hasDir || hasUrl)) {\n return fail(\"INVALID_INPUT\", \"--append cannot be used with --dir or --url\");\n }\n\n if (hasDir) {\n const value = await createSkillpackFromDir(opts.dir as string);\n return { kind: \"skillpack\", value };\n }\n\n if (hasUrl) {\n const value = createSkillrefValue(opts.url as string, opts.path as string, opts.ref as string | undefined);\n return { kind: \"skillref\", value };\n }\n\n if (hasFile) {\n const content = await fs.readFile(opts.file as string, \"utf8\");\n return { kind: \"string\", value: content };\n }\n\n if (hasValue) {\n return { kind: \"string\", value: opts.value as string };\n }\n\n const stdin = await readStdin();\n return { kind: \"string\", value: stdin };\n}\n\nasync function readStdin(): Promise<string> {\n return new Promise((resolve, reject) => {\n let data = \"\";\n process.stdin.setEncoding(\"utf8\");\n process.stdin.on(\"data\", (chunk) => {\n data += chunk;\n });\n process.stdin.on(\"end\", () => resolve(data));\n process.stdin.on(\"error\", reject);\n });\n}\n"],"mappings":"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","names":["import_node_util","import_node_process","import_node_path","import_promises","import_node_fs","path","os","fs","import_node_path","path","import_promises","import_node_path","import_node_os","import_tar","import_promises","import_node_path","import_promises","path","fs","fs","path","import_promises","import_node_path","fs","path","import_node_path","path","tar","fs","path","os","tar","zlib","import_promises","import_node_path","import_node_os","import_node_fs","import_tar","ALLOWED_TYPES","fs","path","os","tar","import_promises","process","fs","process","path","fs"]}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/validators.ts","../src/errors.ts","../src/constants.ts","../src/value.ts","../src/skillpack.ts","../src/fs-ops.ts","../src/chmod.ts","../src/perm.ts","../src/tar-utils.ts","../src/skillref.ts","../src/input.ts"],"sourcesContent":["export { normalizeGithubUrl, validateCommitSha, normalizeSkillPath } from \"./validators\";\nexport { detectSkillValueType } from \"./value\";\nexport { SKILLPACK_HEADER, SKILLREF_HEADER } from \"./constants\";\nexport { createSkillpackFromDir } from \"./skillpack\";\nexport { createSkillrefValue, parseSkillrefValue } from \"./skillref\";\nexport { loadSkillrefToDir } from \"./skillref\";\nexport { resolveSaveInput } from \"./input\";\nexport { CtxbinError, formatError } from \"./errors\";\nexport { safeChmod } from \"./chmod\";\n","import path from \"node:path\";\nimport { fail } from \"./errors\";\n\nexport function normalizeGithubUrl(input: string): string {\n let url: URL;\n try {\n url = new URL(input);\n } catch {\n return fail(\"INVALID_URL\", \"invalid URL\");\n }\n\n if (url.protocol !== \"https:\") {\n return fail(\"INVALID_URL\", \"URL must use https\");\n }\n if (url.hostname !== \"github.com\") {\n return fail(\"INVALID_URL\", \"only github.com is supported\");\n }\n if (url.search || url.hash) {\n return fail(\"INVALID_URL\", \"URL must not include query or hash\");\n }\n\n const parts = url.pathname.split(\"/\").filter(Boolean);\n if (parts.length !== 2) {\n return fail(\"INVALID_URL\", \"URL must be https://github.com/<owner>/<repo>\");\n }\n const owner = parts[0];\n let repo = parts[1];\n if (repo.endsWith(\".git\")) {\n repo = repo.slice(0, -4);\n }\n if (!owner || !repo) {\n return fail(\"INVALID_URL\", \"URL must be https://github.com/<owner>/<repo>\");\n }\n\n return `https://github.com/${owner}/${repo}`;\n}\n\nexport function validateCommitSha(ref: string): string {\n if (!/^[0-9a-f]{40}$/.test(ref)) {\n return fail(\"INVALID_REF\", \"ref must be a 40-hex commit SHA\");\n }\n return ref;\n}\n\nexport function normalizeSkillPath(input: string): string {\n const trimmed = input.trim();\n if (!trimmed) {\n return fail(\"INVALID_PATH\", \"path must be a non-empty directory path\");\n }\n const cleaned = trimmed.replace(/\\\\/g, \"/\");\n if (cleaned.startsWith(\"/\")) {\n return fail(\"INVALID_PATH\", \"path must be relative, not absolute\");\n }\n const normalized = path.posix.normalize(cleaned).replace(/^\\.\\//, \"\");\n if (normalized === \".\" || normalized === \"\") {\n return fail(\"INVALID_PATH\", \"path must be a non-empty directory path\");\n }\n if (normalized.startsWith(\"../\") || normalized.includes(\"/../\") || normalized === \"..\") {\n return fail(\"INVALID_PATH\", \"path must not include .. segments\");\n }\n if (normalized.endsWith(\"/\")) {\n return normalized.slice(0, -1);\n }\n return normalized;\n}\n\nexport function assertSafeTarPath(entryPath: string): void {\n const cleaned = entryPath.replace(/\\\\/g, \"/\");\n if (cleaned.startsWith(\"/\")) {\n return fail(\"INVALID_PATH\", `tar entry path must be relative: ${entryPath}`);\n }\n const normalized = path.posix.normalize(cleaned);\n if (normalized.startsWith(\"../\") || normalized === \"..\" || normalized.includes(\"/../\")) {\n return fail(\"INVALID_PATH\", `tar entry path contains traversal: ${entryPath}`);\n }\n}\n","export type ErrorCode =\n | \"INVALID_INPUT\"\n | \"MISSING_KEY\"\n | \"INVALID_URL\"\n | \"INVALID_REF\"\n | \"INVALID_PATH\"\n | \"NOT_IN_GIT\"\n | \"NOT_FOUND\"\n | \"TYPE_MISMATCH\"\n | \"SIZE_LIMIT\"\n | \"NETWORK\"\n | \"IO\";\n\nexport class CtxbinError extends Error {\n readonly code: ErrorCode;\n\n constructor(code: ErrorCode, message: string) {\n super(message);\n this.code = code;\n }\n}\n\nexport function fail(code: ErrorCode, message: string): never {\n throw new CtxbinError(code, message);\n}\n\nexport function formatError(err: unknown): string {\n if (err instanceof CtxbinError) {\n return `CTXBIN_ERR ${err.code}: ${err.message}`;\n }\n const message = err instanceof Error ? err.message : String(err);\n return `CTXBIN_ERR IO: ${message}`;\n}\n","export const SKILLPACK_HEADER = \"ctxbin-skillpack@1\\n\";\nexport const SKILLREF_HEADER = \"ctxbin-skillref@1\\n\";\n\nexport const MAX_SKILLPACK_BYTES = 7 * 1024 * 1024;\nexport const MAX_SKILLREF_DOWNLOAD_BYTES = 20 * 1024 * 1024;\nexport const MAX_SKILLREF_EXTRACT_BYTES = 100 * 1024 * 1024;\nexport const MAX_SKILLREF_FILES = 5000;\n\nexport const SKILLREF_CONNECT_TIMEOUT_MS = 5000;\nexport const SKILLREF_DOWNLOAD_TIMEOUT_MS = 30000;\n\nexport const DEFAULT_EXCLUDES = [\".git\", \"node_modules\", \".DS_Store\"];\n","import { SKILLPACK_HEADER, SKILLREF_HEADER } from \"./constants\";\n\nexport type SkillValueType = \"skillpack\" | \"skillref\" | \"string\";\n\nexport function detectSkillValueType(value: string): SkillValueType {\n if (value.startsWith(SKILLPACK_HEADER)) return \"skillpack\";\n if (value.startsWith(SKILLREF_HEADER)) return \"skillref\";\n return \"string\";\n}\n","import fs from \"node:fs/promises\";\nimport { createWriteStream } from \"node:fs\";\nimport path from \"node:path\";\nimport os from \"node:os\";\nimport zlib from \"node:zlib\";\nimport { pipeline } from \"node:stream/promises\";\nimport tar from \"tar\";\nimport { DEFAULT_EXCLUDES, MAX_SKILLPACK_BYTES, SKILLPACK_HEADER } from \"./constants\";\nimport { fail } from \"./errors\";\nimport { ensureDir, copyDirContents } from \"./fs-ops\";\nimport { applyNormalizedPermissions } from \"./perm\";\nimport { assertSafeTarPath } from \"./validators\";\nimport { listTarEntries } from \"./tar-utils\";\n\nconst ALLOWED_TYPES = new Set([\"File\", \"Directory\"]);\n\nexport async function createSkillpackFromDir(dirPath: string): Promise<string> {\n const stats = await fs.stat(dirPath).catch(() => null);\n if (!stats || !stats.isDirectory()) {\n return fail(\"INVALID_INPUT\", `--dir is not a directory: ${dirPath}`);\n }\n\n const entries = await collectEntries(dirPath);\n const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), \"ctxbin-skillpack-\"));\n const tarPath = path.join(tmpDir, \"skillpack.tar.gz\");\n\n try {\n const tarStream = tar.c(\n {\n cwd: dirPath,\n portable: true,\n mtime: new Date(0),\n },\n entries\n );\n\n const gzip = zlib.createGzip({ mtime: 0 });\n await pipeline(tarStream, gzip, createWriteStream(tarPath));\n\n const stat = await fs.stat(tarPath);\n if (stat.size > MAX_SKILLPACK_BYTES) {\n return fail(\n \"SIZE_LIMIT\",\n `skillpack tar.gz size ${stat.size} bytes exceeds ${MAX_SKILLPACK_BYTES} bytes`\n );\n }\n\n const data = await fs.readFile(tarPath);\n const b64 = data.toString(\"base64\");\n return SKILLPACK_HEADER + b64;\n } finally {\n await fs.rm(tmpDir, { recursive: true, force: true });\n }\n}\n\nexport async function extractSkillpackToDir(value: string, targetDir: string): Promise<void> {\n const base64 = value.slice(SKILLPACK_HEADER.length);\n let buffer: Buffer;\n try {\n buffer = Buffer.from(base64, \"base64\");\n } catch {\n return fail(\"IO\", \"invalid skillpack base64 data\");\n }\n\n const tmpRoot = await fs.mkdtemp(path.join(os.tmpdir(), \"ctxbin-skillpack-\"));\n const tarPath = path.join(tmpRoot, \"skillpack.tar.gz\");\n await fs.writeFile(tarPath, buffer);\n\n try {\n const entries = await listTarEntries(tarPath);\n const execSet = validateTarEntries(entries);\n\n const extractDir = path.join(tmpRoot, \"extract\");\n await ensureDir(extractDir);\n await tar.x({\n file: tarPath,\n cwd: extractDir,\n preserveOwner: false,\n noMtime: true,\n });\n\n await applyNormalizedPermissions(extractDir, execSet);\n await ensureDir(targetDir);\n await copyDirContents(extractDir, targetDir);\n } finally {\n await fs.rm(tmpRoot, { recursive: true, force: true });\n }\n}\n\nasync function collectEntries(root: string): Promise<string[]> {\n const results: string[] = [];\n\n async function walk(absDir: string, relDir: string): Promise<void> {\n const entries = await fs.readdir(absDir, { withFileTypes: true });\n entries.sort((a, b) => a.name.localeCompare(b.name));\n\n for (const entry of entries) {\n if (DEFAULT_EXCLUDES.includes(entry.name)) {\n if (entry.isDirectory()) {\n continue;\n }\n if (entry.isFile() && entry.name === \".DS_Store\") {\n continue;\n }\n }\n\n const absPath = path.join(absDir, entry.name);\n const relPath = relDir ? path.posix.join(relDir, entry.name) : entry.name;\n const stat = await fs.lstat(absPath);\n if (stat.isSymbolicLink()) {\n return fail(\"IO\", `symlink not allowed in skillpack: ${absPath}`);\n }\n\n if (entry.isDirectory()) {\n results.push(relPath);\n await walk(absPath, relPath);\n continue;\n }\n\n if (entry.isFile()) {\n if (entry.name === \".DS_Store\") {\n continue;\n }\n results.push(relPath);\n continue;\n }\n\n return fail(\"IO\", `unsupported file type in skillpack: ${absPath}`);\n }\n }\n\n await walk(root, \"\");\n results.sort();\n return results;\n}\n\nfunction validateTarEntries(entries: { path: string; type: string; mode: number }[]): Set<string> {\n const execSet = new Set<string>();\n for (const entry of entries) {\n assertSafeTarPath(entry.path);\n if (!ALLOWED_TYPES.has(entry.type)) {\n return fail(\"IO\", `unsupported entry type in skillpack: ${entry.path}`);\n }\n if (entry.type === \"File\" && (entry.mode & 0o111)) {\n execSet.add(entry.path);\n }\n }\n return execSet;\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { fail } from \"./errors\";\nimport { safeChmod } from \"./chmod\";\n\nexport async function ensureDir(dir: string): Promise<void> {\n await fs.mkdir(dir, { recursive: true });\n}\n\nexport function toPosix(p: string): string {\n return p.split(path.sep).join(\"/\");\n}\n\nexport async function copyDirContents(src: string, dest: string): Promise<void> {\n await ensureDir(dest);\n const entries = await fs.readdir(src, { withFileTypes: true });\n for (const entry of entries) {\n const srcPath = path.join(src, entry.name);\n const destPath = path.join(dest, entry.name);\n if (entry.isDirectory()) {\n await copyDirContents(srcPath, destPath);\n const stat = await fs.stat(srcPath);\n await safeChmod(destPath, stat.mode & 0o777);\n continue;\n }\n if (entry.isFile()) {\n await ensureDir(path.dirname(destPath));\n await fs.copyFile(srcPath, destPath);\n const stat = await fs.stat(srcPath);\n await safeChmod(destPath, stat.mode & 0o777);\n continue;\n }\n return fail(\"IO\", `unsupported file type during copy: ${srcPath}`);\n }\n}\n","import fs from \"node:fs/promises\";\n\nexport async function safeChmod(path: string, mode: number): Promise<void> {\n try {\n await fs.chmod(path, mode);\n } catch (err) {\n if (process.platform === \"win32\") {\n return;\n }\n throw err;\n }\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { fail } from \"./errors\";\nimport { toPosix } from \"./fs-ops\";\nimport { safeChmod } from \"./chmod\";\n\nexport async function applyNormalizedPermissions(root: string, execSet: Set<string>): Promise<void> {\n async function walk(absDir: string): Promise<void> {\n const entries = await fs.readdir(absDir, { withFileTypes: true });\n for (const entry of entries) {\n const absPath = path.join(absDir, entry.name);\n if (entry.isDirectory()) {\n await safeChmod(absPath, 0o755);\n await walk(absPath);\n continue;\n }\n if (entry.isFile()) {\n const rel = toPosix(path.relative(root, absPath));\n const mode = execSet.has(rel) ? 0o755 : 0o644;\n await safeChmod(absPath, mode);\n continue;\n }\n return fail(\"IO\", `unsupported file type after extract: ${absPath}`);\n }\n }\n\n await walk(root);\n}\n","import tar from \"tar\";\n\nexport interface TarEntryInfo {\n path: string;\n type: string;\n size: number;\n mode: number;\n}\n\nexport async function listTarEntries(file: string): Promise<TarEntryInfo[]> {\n const entries: TarEntryInfo[] = [];\n await tar.t({\n file,\n onentry(entry) {\n entries.push({\n path: entry.path,\n type: entry.type,\n size: entry.size ?? 0,\n mode: entry.mode ?? 0,\n });\n },\n });\n return entries;\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport os from \"node:os\";\nimport { createWriteStream } from \"node:fs\";\nimport { SKILLREF_HEADER, MAX_SKILLREF_DOWNLOAD_BYTES, MAX_SKILLREF_EXTRACT_BYTES, MAX_SKILLREF_FILES, SKILLREF_CONNECT_TIMEOUT_MS, SKILLREF_DOWNLOAD_TIMEOUT_MS } from \"./constants\";\nimport { fail, CtxbinError } from \"./errors\";\nimport { normalizeGithubUrl, normalizeSkillPath, validateCommitSha, assertSafeTarPath } from \"./validators\";\nimport { listTarEntries, TarEntryInfo } from \"./tar-utils\";\nimport tar from \"tar\";\nimport { ensureDir, copyDirContents } from \"./fs-ops\";\nimport { applyNormalizedPermissions } from \"./perm\";\n\nconst ALLOWED_TYPES = new Set([\"File\", \"Directory\"]);\n\nexport interface Skillref {\n url: string;\n path: string;\n ref?: string;\n track?: \"default\";\n}\n\nexport function createSkillrefValue(url: string, skillPath: string, ref?: string): string {\n const normalizedUrl = normalizeGithubUrl(url);\n const normalizedPath = normalizeSkillPath(skillPath);\n const payload = ref\n ? JSON.stringify({ url: normalizedUrl, path: normalizedPath, ref: validateCommitSha(ref) })\n : JSON.stringify({ url: normalizedUrl, path: normalizedPath, track: \"default\" });\n return SKILLREF_HEADER + payload;\n}\n\nexport function parseSkillrefValue(value: string): Skillref {\n if (!value.startsWith(SKILLREF_HEADER)) {\n return fail(\"TYPE_MISMATCH\", \"value is not a skillref\");\n }\n const raw = value.slice(SKILLREF_HEADER.length);\n let parsed: any;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return fail(\"IO\", \"invalid skillref payload JSON\");\n }\n if (!parsed || typeof parsed.url !== \"string\" || typeof parsed.path !== \"string\") {\n return fail(\"IO\", \"invalid skillref payload fields\");\n }\n const normalized = {\n url: normalizeGithubUrl(parsed.url),\n path: normalizeSkillPath(parsed.path),\n } satisfies Pick<Skillref, \"url\" | \"path\">;\n\n if (typeof parsed.ref === \"string\") {\n return { ...normalized, ref: validateCommitSha(parsed.ref) };\n }\n\n if (parsed.track === \"default\") {\n return { ...normalized, track: \"default\" };\n }\n\n return fail(\"IO\", \"invalid skillref payload fields\");\n}\n\nexport async function loadSkillrefToDir(value: string, targetDir: string): Promise<void> {\n const skillref = parseSkillrefValue(value);\n const resolvedRef = skillref.ref ?? (await fetchDefaultBranch(skillref.url));\n const tmpRoot = await fs.mkdtemp(path.join(os.tmpdir(), \"ctxbin-skillref-\"));\n const tarPath = path.join(tmpRoot, \"skillref.tar.gz\");\n\n try {\n await downloadArchive(skillref.url, resolvedRef, tarPath);\n\n const entries = await listTarEntries(tarPath).catch(() => fail(\"IO\", \"failed to parse tar archive\"));\n const analysis = analyzeEntries(entries, skillref.path);\n\n const extractDir = path.join(tmpRoot, \"extract\");\n await ensureDir(extractDir);\n\n const stripCount = 1 + skillref.path.split(\"/\").length;\n await tar.x({\n file: tarPath,\n cwd: extractDir,\n preserveOwner: false,\n noMtime: true,\n strip: stripCount,\n filter: (p, entry) => {\n const entryPath = entry?.path ?? p;\n return isUnderPath(entryPath, analysis.prefix, skillref.path);\n },\n });\n\n await applyNormalizedPermissions(extractDir, analysis.execSet);\n await ensureDir(targetDir);\n await copyDirContents(extractDir, targetDir);\n } finally {\n await fs.rm(tmpRoot, { recursive: true, force: true });\n }\n}\n\nasync function downloadArchive(repoUrl: string, ref: string, outPath: string): Promise<void> {\n const { owner, repo } = splitGithubUrl(repoUrl);\n const url = `https://codeload.github.com/${owner}/${repo}/tar.gz/${ref}`;\n const controller = new AbortController();\n const totalTimer = setTimeout(() => controller.abort(), SKILLREF_DOWNLOAD_TIMEOUT_MS);\n let res: Response;\n try {\n res = await fetchWithRedirect(url, 1, controller, [\"github.com\", \"codeload.github.com\"]);\n } catch (err) {\n clearTimeout(totalTimer);\n return fail(\"NETWORK\", `download failed: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n if (!res.ok) {\n clearTimeout(totalTimer);\n const text = await res.text();\n return fail(\"NETWORK\", `download failed (${res.status}): ${text}`);\n }\n if (!res.body) {\n clearTimeout(totalTimer);\n return fail(\"NETWORK\", \"download failed: empty response body\");\n }\n\n const fileStream = createWriteStream(outPath);\n let total = 0;\n let magic = Buffer.alloc(0);\n\n try {\n for await (const chunk of res.body as AsyncIterable<Buffer>) {\n if (magic.length < 2) {\n const needed = 2 - magic.length;\n magic = Buffer.concat([magic, chunk.subarray(0, needed)]);\n if (magic.length === 2) {\n if (magic[0] !== 0x1f || magic[1] !== 0x8b) {\n fileStream.close();\n controller.abort();\n return fail(\"IO\", \"downloaded file is not gzip data\");\n }\n }\n }\n\n total += chunk.length;\n if (total > MAX_SKILLREF_DOWNLOAD_BYTES) {\n fileStream.close();\n controller.abort();\n return fail(\n \"SIZE_LIMIT\",\n `downloaded archive size ${total} exceeds ${MAX_SKILLREF_DOWNLOAD_BYTES} bytes`\n );\n }\n\n fileStream.write(chunk);\n }\n } catch (err) {\n fileStream.close();\n clearTimeout(totalTimer);\n if (err instanceof CtxbinError) {\n throw err;\n }\n return fail(\"NETWORK\", `download failed: ${err instanceof Error ? err.message : String(err)}`);\n } finally {\n clearTimeout(totalTimer);\n }\n\n if (magic.length < 2) {\n fileStream.close();\n return fail(\"IO\", \"downloaded file is incomplete\");\n }\n\n await new Promise<void>((resolve, reject) => {\n fileStream.end(() => resolve());\n fileStream.on(\"error\", reject);\n });\n}\n\nasync function fetchWithRedirect(\n url: string,\n redirectsLeft: number,\n controller: AbortController,\n allowedHosts: string[],\n init?: RequestInit\n): Promise<Response> {\n const connectTimer = setTimeout(() => controller.abort(), SKILLREF_CONNECT_TIMEOUT_MS);\n\n const res = await fetch(url, {\n ...init,\n signal: controller.signal,\n redirect: \"manual\",\n });\n\n clearTimeout(connectTimer);\n\n if (isRedirect(res.status)) {\n if (redirectsLeft <= 0) {\n return fail(\"NETWORK\", \"too many redirects\");\n }\n const location = res.headers.get(\"location\");\n if (!location) {\n return fail(\"NETWORK\", \"redirect without location header\");\n }\n const nextUrl = new URL(location, url).toString();\n const host = new URL(nextUrl).hostname;\n if (!allowedHosts.includes(host)) {\n return fail(\"NETWORK\", `redirected to unsupported host: ${host}`);\n }\n return fetchWithRedirect(nextUrl, redirectsLeft - 1, controller, allowedHosts, init);\n }\n\n return res;\n}\n\nfunction isRedirect(status: number): boolean {\n return [301, 302, 303, 307, 308].includes(status);\n}\n\nfunction splitGithubUrl(repoUrl: string): { owner: string; repo: string } {\n const url = new URL(repoUrl);\n const parts = url.pathname.split(\"/\").filter(Boolean);\n if (parts.length !== 2) {\n return fail(\"INVALID_URL\", \"URL must be https://github.com/<owner>/<repo>\");\n }\n return { owner: parts[0], repo: parts[1] };\n}\n\nasync function fetchDefaultBranch(repoUrl: string): Promise<string> {\n const { owner, repo } = splitGithubUrl(repoUrl);\n const url = `https://api.github.com/repos/${owner}/${repo}`;\n const controller = new AbortController();\n const totalTimer = setTimeout(() => controller.abort(), SKILLREF_DOWNLOAD_TIMEOUT_MS);\n let res: Response;\n try {\n res = await fetchWithRedirect(url, 1, controller, [\"github.com\", \"api.github.com\"], {\n headers: {\n \"User-Agent\": \"ctxbin\",\n Accept: \"application/vnd.github+json\",\n },\n });\n } catch (err) {\n clearTimeout(totalTimer);\n return fail(\"NETWORK\", `default branch lookup failed: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n if (!res.ok) {\n clearTimeout(totalTimer);\n const text = await res.text();\n return fail(\"NETWORK\", `default branch lookup failed (${res.status}): ${text}`);\n }\n\n let data: any;\n try {\n data = await res.json();\n } catch {\n clearTimeout(totalTimer);\n return fail(\"NETWORK\", \"default branch lookup returned invalid JSON\");\n }\n\n clearTimeout(totalTimer);\n if (!data || typeof data.default_branch !== \"string\" || data.default_branch.length === 0) {\n return fail(\"NETWORK\", \"default branch lookup returned no default_branch\");\n }\n return data.default_branch;\n}\n\nfunction analyzeEntries(entries: TarEntryInfo[], requestedPath: string): {\n prefix: string;\n execSet: Set<string>;\n} {\n if (entries.length === 0) {\n return fail(\"NOT_FOUND\", \"archive contained no entries\");\n }\n\n const prefix = entries[0].path.split(\"/\")[0];\n if (!prefix) {\n return fail(\"IO\", \"unable to determine archive prefix\");\n }\n\n const execSet = new Set<string>();\n let entryCount = 0;\n let totalSize = 0;\n let matched = false;\n\n for (const entry of entries) {\n assertSafeTarPath(entry.path);\n if (!ALLOWED_TYPES.has(entry.type)) {\n return fail(\"IO\", `unsupported entry type in archive: ${entry.path}`);\n }\n\n if (entry.path === prefix) {\n continue;\n }\n if (!entry.path.startsWith(`${prefix}/`)) {\n return fail(\"IO\", \"archive has unexpected top-level layout\");\n }\n\n const rel = entry.path.slice(prefix.length + 1);\n if (!rel) {\n continue;\n }\n\n const relToReq = stripRequestedPath(rel, requestedPath);\n if (relToReq === null) {\n continue;\n }\n\n matched = true;\n entryCount += 1;\n if (rel === requestedPath && entry.type === \"File\") {\n return fail(\"INVALID_PATH\", \"requested path is not a directory\");\n }\n if (entry.type === \"File\") {\n totalSize += entry.size ?? 0;\n if (entry.mode & 0o111) {\n execSet.add(relToReq);\n }\n }\n }\n\n if (!matched) {\n return fail(\"NOT_FOUND\", \"requested path not found in archive\");\n }\n if (entryCount > MAX_SKILLREF_FILES) {\n return fail(\"SIZE_LIMIT\", `extracted entry count ${entryCount} exceeds ${MAX_SKILLREF_FILES}`);\n }\n if (totalSize > MAX_SKILLREF_EXTRACT_BYTES) {\n return fail(\"SIZE_LIMIT\", `extracted size ${totalSize} exceeds ${MAX_SKILLREF_EXTRACT_BYTES}`);\n }\n\n return { prefix, execSet };\n}\n\nfunction stripRequestedPath(rel: string, requestedPath: string): string | null {\n if (rel === requestedPath) {\n return \"\";\n }\n const prefix = requestedPath + \"/\";\n if (rel.startsWith(prefix)) {\n return rel.slice(prefix.length);\n }\n return null;\n}\n\nfunction isUnderPath(entryPath: string, prefix: string, requestedPath: string): boolean {\n if (entryPath === prefix) {\n return false;\n }\n if (!entryPath.startsWith(`${prefix}/`)) {\n return false;\n }\n const rel = entryPath.slice(prefix.length + 1);\n if (!rel) {\n return false;\n }\n if (rel === requestedPath || rel.startsWith(requestedPath + \"/\")) {\n return true;\n }\n return false;\n}\n","import fs from \"node:fs/promises\";\nimport process from \"node:process\";\nimport { fail } from \"./errors\";\nimport { createSkillpackFromDir } from \"./skillpack\";\nimport { createSkillrefValue } from \"./skillref\";\n\nexport type SaveInput = { kind: \"string\" | \"skillpack\" | \"skillref\"; value: string };\n\nexport interface SaveOptions {\n append: boolean;\n file?: string;\n value?: string;\n dir?: string;\n url?: string;\n ref?: string;\n path?: string;\n}\n\nexport async function resolveSaveInput(\n resource: string,\n opts: SaveOptions,\n stdinIsTTY: boolean = Boolean(process.stdin.isTTY)\n): Promise<SaveInput> {\n const hasFile = typeof opts.file === \"string\";\n const hasValue = typeof opts.value === \"string\";\n const hasDir = typeof opts.dir === \"string\";\n const urlFlagsUsed = Boolean(opts.url || opts.ref || opts.path);\n const hasUrl = Boolean(opts.url && opts.path);\n const explicitCount = [hasFile, hasValue, hasDir, hasUrl].filter(Boolean).length;\n const hasStdin = !stdinIsTTY && explicitCount === 0;\n\n if (urlFlagsUsed && !hasUrl) {\n return fail(\"INVALID_INPUT\", \"--url and --path must be provided together\");\n }\n\n const methods = explicitCount + (hasStdin ? 1 : 0);\n if (methods !== 1) {\n return fail(\"INVALID_INPUT\", \"exactly one input method must be used\");\n }\n\n if (hasDir && resource !== \"skill\") {\n return fail(\"INVALID_INPUT\", \"--dir is only valid for skill save\");\n }\n if (hasUrl && resource !== \"skill\") {\n return fail(\"INVALID_INPUT\", \"--url/--ref/--path are only valid for skill save\");\n }\n if (opts.append && (hasDir || hasUrl)) {\n return fail(\"INVALID_INPUT\", \"--append cannot be used with --dir or --url\");\n }\n\n if (hasDir) {\n const value = await createSkillpackFromDir(opts.dir as string);\n return { kind: \"skillpack\", value };\n }\n\n if (hasUrl) {\n const value = createSkillrefValue(opts.url as string, opts.path as string, opts.ref as string | undefined);\n return { kind: \"skillref\", value };\n }\n\n if (hasFile) {\n const content = await fs.readFile(opts.file as string, \"utf8\");\n return { kind: \"string\", value: content };\n }\n\n if (hasValue) {\n return { kind: \"string\", value: opts.value as string };\n }\n\n const stdin = await readStdin();\n return { kind: \"string\", value: stdin };\n}\n\nasync function readStdin(): Promise<string> {\n return new Promise((resolve, reject) => {\n let data = \"\";\n process.stdin.setEncoding(\"utf8\");\n process.stdin.on(\"data\", (chunk) => {\n data += chunk;\n });\n process.stdin.on(\"end\", () => resolve(data));\n process.stdin.on(\"error\", reject);\n });\n}\n"],"mappings":"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","names":["path","import_promises","import_node_path","import_tar","import_promises","import_node_path","path","fs","fs","path","import_promises","import_node_path","fs","path","tar","fs","path","os","tar","zlib","fs","path","import_promises","import_node_path","import_node_os","import_node_fs","import_tar","fs","path","os","tar","import_promises","process","fs"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/validators.ts","../src/errors.ts","../src/constants.ts","../src/value.ts","../src/skillpack.ts","../src/fs-ops.ts","../src/chmod.ts","../src/perm.ts","../src/tar-utils.ts","../src/skillref.ts","../src/input.ts"],"sourcesContent":["export { normalizeGithubUrl, validateCommitSha, normalizeSkillPath } from \"./validators\";\nexport { detectSkillValueType } from \"./value\";\nexport { SKILLPACK_HEADER, SKILLREF_HEADER } from \"./constants\";\nexport { createSkillpackFromDir } from \"./skillpack\";\nexport { createSkillrefValue, parseSkillrefValue } from \"./skillref\";\nexport { loadSkillrefToDir } from \"./skillref\";\nexport { resolveSaveInput } from \"./input\";\nexport { CtxbinError, formatError } from \"./errors\";\nexport { safeChmod } from \"./chmod\";\n","import path from \"node:path\";\nimport { fail } from \"./errors\";\n\nexport function normalizeGithubUrl(input: string): string {\n let url: URL;\n try {\n url = new URL(input);\n } catch {\n return fail(\"INVALID_URL\", \"invalid URL\");\n }\n\n if (url.protocol !== \"https:\") {\n return fail(\"INVALID_URL\", \"URL must use https\");\n }\n if (url.hostname !== \"github.com\") {\n return fail(\"INVALID_URL\", \"only github.com is supported\");\n }\n if (url.search || url.hash) {\n return fail(\"INVALID_URL\", \"URL must not include query or hash\");\n }\n\n const parts = url.pathname.split(\"/\").filter(Boolean);\n if (parts.length !== 2) {\n return fail(\"INVALID_URL\", \"URL must be https://github.com/<owner>/<repo>\");\n }\n const owner = parts[0];\n let repo = parts[1];\n if (repo.endsWith(\".git\")) {\n repo = repo.slice(0, -4);\n }\n if (!owner || !repo) {\n return fail(\"INVALID_URL\", \"URL must be https://github.com/<owner>/<repo>\");\n }\n\n return `https://github.com/${owner}/${repo}`;\n}\n\nexport function validateCommitSha(ref: string): string {\n if (!/^[0-9a-f]{40}$/.test(ref)) {\n return fail(\"INVALID_REF\", \"ref must be a 40-hex commit SHA\");\n }\n return ref;\n}\n\nexport function normalizeSkillPath(input: string): string {\n const trimmed = input.trim();\n if (!trimmed) {\n return fail(\"INVALID_PATH\", \"path must be a non-empty directory path\");\n }\n const cleaned = trimmed.replace(/\\\\/g, \"/\");\n if (cleaned.startsWith(\"/\")) {\n return fail(\"INVALID_PATH\", \"path must be relative, not absolute\");\n }\n const normalized = path.posix.normalize(cleaned).replace(/^\\.\\//, \"\");\n if (normalized === \".\" || normalized === \"\") {\n return fail(\"INVALID_PATH\", \"path must be a non-empty directory path\");\n }\n if (normalized.startsWith(\"../\") || normalized.includes(\"/../\") || normalized === \"..\") {\n return fail(\"INVALID_PATH\", \"path must not include .. segments\");\n }\n if (normalized.endsWith(\"/\")) {\n return normalized.slice(0, -1);\n }\n return normalized;\n}\n\nexport function assertSafeTarPath(entryPath: string): void {\n const cleaned = entryPath.replace(/\\\\/g, \"/\");\n if (cleaned.startsWith(\"/\")) {\n return fail(\"INVALID_PATH\", `tar entry path must be relative: ${entryPath}`);\n }\n const normalized = path.posix.normalize(cleaned);\n if (normalized.startsWith(\"../\") || normalized === \"..\" || normalized.includes(\"/../\")) {\n return fail(\"INVALID_PATH\", `tar entry path contains traversal: ${entryPath}`);\n }\n}\n","export type ErrorCode =\n | \"INVALID_INPUT\"\n | \"MISSING_KEY\"\n | \"INVALID_URL\"\n | \"INVALID_REF\"\n | \"INVALID_PATH\"\n | \"NOT_IN_GIT\"\n | \"NOT_FOUND\"\n | \"TYPE_MISMATCH\"\n | \"SIZE_LIMIT\"\n | \"NETWORK\"\n | \"IO\";\n\nexport class CtxbinError extends Error {\n readonly code: ErrorCode;\n\n constructor(code: ErrorCode, message: string) {\n super(message);\n this.code = code;\n }\n}\n\nexport function fail(code: ErrorCode, message: string): never {\n throw new CtxbinError(code, message);\n}\n\nexport function formatError(err: unknown): string {\n if (err instanceof CtxbinError) {\n return `CTXBIN_ERR ${err.code}: ${err.message}`;\n }\n const message = err instanceof Error ? err.message : String(err);\n return `CTXBIN_ERR IO: ${message}`;\n}\n","export const SKILLPACK_HEADER = \"ctxbin-skillpack@1\\n\";\nexport const SKILLREF_HEADER = \"ctxbin-skillref@1\\n\";\n\nexport const MAX_SKILLPACK_BYTES = 7 * 1024 * 1024;\nexport const MAX_SKILLREF_DOWNLOAD_BYTES = 20 * 1024 * 1024;\nexport const MAX_SKILLREF_EXTRACT_BYTES = 100 * 1024 * 1024;\nexport const MAX_SKILLREF_FILES = 5000;\n\nexport const SKILLREF_CONNECT_TIMEOUT_MS = 5000;\nexport const SKILLREF_DOWNLOAD_TIMEOUT_MS = 30000;\n\nexport const DEFAULT_EXCLUDES = [\".git\", \"node_modules\", \".DS_Store\"];\n\nexport const STORE_REQUEST_TIMEOUT_MS = 5000;\n","import { SKILLPACK_HEADER, SKILLREF_HEADER } from \"./constants\";\n\nexport type SkillValueType = \"skillpack\" | \"skillref\" | \"string\";\n\nexport function detectSkillValueType(value: string): SkillValueType {\n if (value.startsWith(SKILLPACK_HEADER)) return \"skillpack\";\n if (value.startsWith(SKILLREF_HEADER)) return \"skillref\";\n return \"string\";\n}\n","import fs from \"node:fs/promises\";\nimport { createWriteStream } from \"node:fs\";\nimport path from \"node:path\";\nimport os from \"node:os\";\nimport zlib from \"node:zlib\";\nimport { pipeline } from \"node:stream/promises\";\nimport tar from \"tar\";\nimport { DEFAULT_EXCLUDES, MAX_SKILLPACK_BYTES, SKILLPACK_HEADER } from \"./constants\";\nimport { fail } from \"./errors\";\nimport { ensureDir, copyDirContents } from \"./fs-ops\";\nimport { applyNormalizedPermissions } from \"./perm\";\nimport { assertSafeTarPath } from \"./validators\";\nimport { listTarEntries } from \"./tar-utils\";\n\nconst ALLOWED_TYPES = new Set([\"File\", \"Directory\"]);\n\nexport async function createSkillpackFromDir(dirPath: string): Promise<string> {\n const stats = await fs.stat(dirPath).catch(() => null);\n if (!stats || !stats.isDirectory()) {\n return fail(\"INVALID_INPUT\", `--dir is not a directory: ${dirPath}`);\n }\n\n const entries = await collectEntries(dirPath);\n const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), \"ctxbin-skillpack-\"));\n const tarPath = path.join(tmpDir, \"skillpack.tar.gz\");\n\n try {\n const tarStream = tar.c(\n {\n cwd: dirPath,\n portable: true,\n mtime: new Date(0),\n },\n entries\n );\n\n const gzip = zlib.createGzip({ mtime: 0 });\n await pipeline(tarStream, gzip, createWriteStream(tarPath));\n\n const stat = await fs.stat(tarPath);\n if (stat.size > MAX_SKILLPACK_BYTES) {\n return fail(\n \"SIZE_LIMIT\",\n `skillpack tar.gz size ${stat.size} bytes exceeds ${MAX_SKILLPACK_BYTES} bytes`\n );\n }\n\n const data = await fs.readFile(tarPath);\n const b64 = data.toString(\"base64\");\n return SKILLPACK_HEADER + b64;\n } finally {\n await fs.rm(tmpDir, { recursive: true, force: true });\n }\n}\n\nexport async function extractSkillpackToDir(value: string, targetDir: string): Promise<void> {\n const base64 = value.slice(SKILLPACK_HEADER.length);\n let buffer: Buffer;\n try {\n buffer = Buffer.from(base64, \"base64\");\n } catch {\n return fail(\"IO\", \"invalid skillpack base64 data\");\n }\n\n const tmpRoot = await fs.mkdtemp(path.join(os.tmpdir(), \"ctxbin-skillpack-\"));\n const tarPath = path.join(tmpRoot, \"skillpack.tar.gz\");\n await fs.writeFile(tarPath, buffer);\n\n try {\n const entries = await listTarEntries(tarPath);\n const execSet = validateTarEntries(entries);\n\n const extractDir = path.join(tmpRoot, \"extract\");\n await ensureDir(extractDir);\n await tar.x({\n file: tarPath,\n cwd: extractDir,\n preserveOwner: false,\n noMtime: true,\n });\n\n await applyNormalizedPermissions(extractDir, execSet);\n await ensureDir(targetDir);\n await copyDirContents(extractDir, targetDir);\n } finally {\n await fs.rm(tmpRoot, { recursive: true, force: true });\n }\n}\n\nasync function collectEntries(root: string): Promise<string[]> {\n const results: string[] = [];\n\n async function walk(absDir: string, relDir: string): Promise<void> {\n const entries = await fs.readdir(absDir, { withFileTypes: true });\n entries.sort((a, b) => a.name.localeCompare(b.name));\n\n for (const entry of entries) {\n if (DEFAULT_EXCLUDES.includes(entry.name)) {\n if (entry.isDirectory()) {\n continue;\n }\n if (entry.isFile() && entry.name === \".DS_Store\") {\n continue;\n }\n }\n\n const absPath = path.join(absDir, entry.name);\n const relPath = relDir ? path.posix.join(relDir, entry.name) : entry.name;\n const stat = await fs.lstat(absPath);\n if (stat.isSymbolicLink()) {\n return fail(\"IO\", `symlink not allowed in skillpack: ${absPath}`);\n }\n\n if (entry.isDirectory()) {\n results.push(relPath);\n await walk(absPath, relPath);\n continue;\n }\n\n if (entry.isFile()) {\n if (entry.name === \".DS_Store\") {\n continue;\n }\n results.push(relPath);\n continue;\n }\n\n return fail(\"IO\", `unsupported file type in skillpack: ${absPath}`);\n }\n }\n\n await walk(root, \"\");\n results.sort();\n return results;\n}\n\nfunction validateTarEntries(entries: { path: string; type: string; mode: number }[]): Set<string> {\n const execSet = new Set<string>();\n for (const entry of entries) {\n assertSafeTarPath(entry.path);\n if (!ALLOWED_TYPES.has(entry.type)) {\n return fail(\"IO\", `unsupported entry type in skillpack: ${entry.path}`);\n }\n if (entry.type === \"File\" && (entry.mode & 0o111)) {\n execSet.add(entry.path);\n }\n }\n return execSet;\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { fail } from \"./errors\";\nimport { safeChmod } from \"./chmod\";\n\nexport async function ensureDir(dir: string): Promise<void> {\n await fs.mkdir(dir, { recursive: true });\n}\n\nexport function toPosix(p: string): string {\n return p.split(path.sep).join(\"/\");\n}\n\nexport async function copyDirContents(src: string, dest: string): Promise<void> {\n await ensureDir(dest);\n const entries = await fs.readdir(src, { withFileTypes: true });\n for (const entry of entries) {\n const srcPath = path.join(src, entry.name);\n const destPath = path.join(dest, entry.name);\n if (entry.isDirectory()) {\n await copyDirContents(srcPath, destPath);\n const stat = await fs.stat(srcPath);\n await safeChmod(destPath, stat.mode & 0o777);\n continue;\n }\n if (entry.isFile()) {\n await ensureDir(path.dirname(destPath));\n await fs.copyFile(srcPath, destPath);\n const stat = await fs.stat(srcPath);\n await safeChmod(destPath, stat.mode & 0o777);\n continue;\n }\n return fail(\"IO\", `unsupported file type during copy: ${srcPath}`);\n }\n}\n","import fs from \"node:fs/promises\";\n\nexport async function safeChmod(path: string, mode: number): Promise<void> {\n try {\n await fs.chmod(path, mode);\n } catch (err) {\n if (process.platform === \"win32\") {\n return;\n }\n throw err;\n }\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { fail } from \"./errors\";\nimport { toPosix } from \"./fs-ops\";\nimport { safeChmod } from \"./chmod\";\n\nexport async function applyNormalizedPermissions(root: string, execSet: Set<string>): Promise<void> {\n async function walk(absDir: string): Promise<void> {\n const entries = await fs.readdir(absDir, { withFileTypes: true });\n for (const entry of entries) {\n const absPath = path.join(absDir, entry.name);\n if (entry.isDirectory()) {\n await safeChmod(absPath, 0o755);\n await walk(absPath);\n continue;\n }\n if (entry.isFile()) {\n const rel = toPosix(path.relative(root, absPath));\n const mode = execSet.has(rel) ? 0o755 : 0o644;\n await safeChmod(absPath, mode);\n continue;\n }\n return fail(\"IO\", `unsupported file type after extract: ${absPath}`);\n }\n }\n\n await walk(root);\n}\n","import tar from \"tar\";\n\nexport interface TarEntryInfo {\n path: string;\n type: string;\n size: number;\n mode: number;\n}\n\nexport async function listTarEntries(file: string): Promise<TarEntryInfo[]> {\n const entries: TarEntryInfo[] = [];\n await tar.t({\n file,\n onentry(entry) {\n entries.push({\n path: entry.path,\n type: entry.type,\n size: entry.size ?? 0,\n mode: entry.mode ?? 0,\n });\n },\n });\n return entries;\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport os from \"node:os\";\nimport { createWriteStream } from \"node:fs\";\nimport { SKILLREF_HEADER, MAX_SKILLREF_DOWNLOAD_BYTES, MAX_SKILLREF_EXTRACT_BYTES, MAX_SKILLREF_FILES, SKILLREF_CONNECT_TIMEOUT_MS, SKILLREF_DOWNLOAD_TIMEOUT_MS } from \"./constants\";\nimport { fail, CtxbinError } from \"./errors\";\nimport { normalizeGithubUrl, normalizeSkillPath, validateCommitSha, assertSafeTarPath } from \"./validators\";\nimport { listTarEntries, TarEntryInfo } from \"./tar-utils\";\nimport tar from \"tar\";\nimport { ensureDir, copyDirContents } from \"./fs-ops\";\nimport { applyNormalizedPermissions } from \"./perm\";\n\nconst ALLOWED_TYPES = new Set([\"File\", \"Directory\"]);\n\nexport interface Skillref {\n url: string;\n path: string;\n ref?: string;\n track?: \"default\";\n}\n\nexport function createSkillrefValue(url: string, skillPath: string, ref?: string): string {\n const normalizedUrl = normalizeGithubUrl(url);\n const normalizedPath = normalizeSkillPath(skillPath);\n const payload = ref\n ? JSON.stringify({ url: normalizedUrl, path: normalizedPath, ref: validateCommitSha(ref) })\n : JSON.stringify({ url: normalizedUrl, path: normalizedPath, track: \"default\" });\n return SKILLREF_HEADER + payload;\n}\n\nexport function parseSkillrefValue(value: string): Skillref {\n if (!value.startsWith(SKILLREF_HEADER)) {\n return fail(\"TYPE_MISMATCH\", \"value is not a skillref\");\n }\n const raw = value.slice(SKILLREF_HEADER.length);\n let parsed: any;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return fail(\"IO\", \"invalid skillref payload JSON\");\n }\n if (!parsed || typeof parsed.url !== \"string\" || typeof parsed.path !== \"string\") {\n return fail(\"IO\", \"invalid skillref payload fields\");\n }\n const normalized = {\n url: normalizeGithubUrl(parsed.url),\n path: normalizeSkillPath(parsed.path),\n } satisfies Pick<Skillref, \"url\" | \"path\">;\n\n if (typeof parsed.ref === \"string\") {\n return { ...normalized, ref: validateCommitSha(parsed.ref) };\n }\n\n if (parsed.track === \"default\") {\n return { ...normalized, track: \"default\" };\n }\n\n return fail(\"IO\", \"invalid skillref payload fields\");\n}\n\nexport async function loadSkillrefToDir(value: string, targetDir: string): Promise<void> {\n const skillref = parseSkillrefValue(value);\n const resolvedRef = skillref.ref ?? (await fetchDefaultBranch(skillref.url));\n const tmpRoot = await fs.mkdtemp(path.join(os.tmpdir(), \"ctxbin-skillref-\"));\n const tarPath = path.join(tmpRoot, \"skillref.tar.gz\");\n\n try {\n await downloadArchive(skillref.url, resolvedRef, tarPath);\n\n const entries = await listTarEntries(tarPath).catch(() => fail(\"IO\", \"failed to parse tar archive\"));\n const analysis = analyzeEntries(entries, skillref.path);\n\n const extractDir = path.join(tmpRoot, \"extract\");\n await ensureDir(extractDir);\n\n const stripCount = 1 + skillref.path.split(\"/\").length;\n await tar.x({\n file: tarPath,\n cwd: extractDir,\n preserveOwner: false,\n noMtime: true,\n strip: stripCount,\n filter: (p, entry) => {\n const entryPath = entry?.path ?? p;\n return isUnderPath(entryPath, analysis.prefix, skillref.path);\n },\n });\n\n await applyNormalizedPermissions(extractDir, analysis.execSet);\n await ensureDir(targetDir);\n await copyDirContents(extractDir, targetDir);\n } finally {\n await fs.rm(tmpRoot, { recursive: true, force: true });\n }\n}\n\nasync function downloadArchive(repoUrl: string, ref: string, outPath: string): Promise<void> {\n const { owner, repo } = splitGithubUrl(repoUrl);\n const url = `https://codeload.github.com/${owner}/${repo}/tar.gz/${ref}`;\n const controller = new AbortController();\n const totalTimer = setTimeout(() => controller.abort(), SKILLREF_DOWNLOAD_TIMEOUT_MS);\n let res: Response;\n try {\n res = await fetchWithRedirect(url, 1, controller, [\"github.com\", \"codeload.github.com\"]);\n } catch (err) {\n clearTimeout(totalTimer);\n return fail(\"NETWORK\", `download failed: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n if (!res.ok) {\n clearTimeout(totalTimer);\n const text = await res.text();\n return fail(\"NETWORK\", `download failed (${res.status}): ${text}`);\n }\n if (!res.body) {\n clearTimeout(totalTimer);\n return fail(\"NETWORK\", \"download failed: empty response body\");\n }\n\n const fileStream = createWriteStream(outPath);\n let total = 0;\n let magic = Buffer.alloc(0);\n\n try {\n for await (const chunk of res.body as AsyncIterable<Buffer>) {\n if (magic.length < 2) {\n const needed = 2 - magic.length;\n magic = Buffer.concat([magic, chunk.subarray(0, needed)]);\n if (magic.length === 2) {\n if (magic[0] !== 0x1f || magic[1] !== 0x8b) {\n fileStream.close();\n controller.abort();\n return fail(\"IO\", \"downloaded file is not gzip data\");\n }\n }\n }\n\n total += chunk.length;\n if (total > MAX_SKILLREF_DOWNLOAD_BYTES) {\n fileStream.close();\n controller.abort();\n return fail(\n \"SIZE_LIMIT\",\n `downloaded archive size ${total} exceeds ${MAX_SKILLREF_DOWNLOAD_BYTES} bytes`\n );\n }\n\n fileStream.write(chunk);\n }\n } catch (err) {\n fileStream.close();\n clearTimeout(totalTimer);\n if (err instanceof CtxbinError) {\n throw err;\n }\n return fail(\"NETWORK\", `download failed: ${err instanceof Error ? err.message : String(err)}`);\n } finally {\n clearTimeout(totalTimer);\n }\n\n if (magic.length < 2) {\n fileStream.close();\n return fail(\"IO\", \"downloaded file is incomplete\");\n }\n\n await new Promise<void>((resolve, reject) => {\n fileStream.end(() => resolve());\n fileStream.on(\"error\", reject);\n });\n}\n\nasync function fetchWithRedirect(\n url: string,\n redirectsLeft: number,\n controller: AbortController,\n allowedHosts: string[],\n init?: RequestInit\n): Promise<Response> {\n const connectTimer = setTimeout(() => controller.abort(), SKILLREF_CONNECT_TIMEOUT_MS);\n\n const res = await fetch(url, {\n ...init,\n signal: controller.signal,\n redirect: \"manual\",\n });\n\n clearTimeout(connectTimer);\n\n if (isRedirect(res.status)) {\n if (redirectsLeft <= 0) {\n return fail(\"NETWORK\", \"too many redirects\");\n }\n const location = res.headers.get(\"location\");\n if (!location) {\n return fail(\"NETWORK\", \"redirect without location header\");\n }\n const nextUrl = new URL(location, url).toString();\n const host = new URL(nextUrl).hostname;\n if (!allowedHosts.includes(host)) {\n return fail(\"NETWORK\", `redirected to unsupported host: ${host}`);\n }\n return fetchWithRedirect(nextUrl, redirectsLeft - 1, controller, allowedHosts, init);\n }\n\n return res;\n}\n\nfunction isRedirect(status: number): boolean {\n return [301, 302, 303, 307, 308].includes(status);\n}\n\nfunction splitGithubUrl(repoUrl: string): { owner: string; repo: string } {\n const url = new URL(repoUrl);\n const parts = url.pathname.split(\"/\").filter(Boolean);\n if (parts.length !== 2) {\n return fail(\"INVALID_URL\", \"URL must be https://github.com/<owner>/<repo>\");\n }\n return { owner: parts[0], repo: parts[1] };\n}\n\nasync function fetchDefaultBranch(repoUrl: string): Promise<string> {\n const { owner, repo } = splitGithubUrl(repoUrl);\n const url = `https://api.github.com/repos/${owner}/${repo}`;\n const controller = new AbortController();\n const totalTimer = setTimeout(() => controller.abort(), SKILLREF_DOWNLOAD_TIMEOUT_MS);\n let res: Response;\n try {\n res = await fetchWithRedirect(url, 1, controller, [\"github.com\", \"api.github.com\"], {\n headers: {\n \"User-Agent\": \"ctxbin\",\n Accept: \"application/vnd.github+json\",\n },\n });\n } catch (err) {\n clearTimeout(totalTimer);\n return fail(\"NETWORK\", `default branch lookup failed: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n if (!res.ok) {\n clearTimeout(totalTimer);\n const text = await res.text();\n return fail(\"NETWORK\", `default branch lookup failed (${res.status}): ${text}`);\n }\n\n let data: any;\n try {\n data = await res.json();\n } catch {\n clearTimeout(totalTimer);\n return fail(\"NETWORK\", \"default branch lookup returned invalid JSON\");\n }\n\n clearTimeout(totalTimer);\n if (!data || typeof data.default_branch !== \"string\" || data.default_branch.length === 0) {\n return fail(\"NETWORK\", \"default branch lookup returned no default_branch\");\n }\n return data.default_branch;\n}\n\nfunction analyzeEntries(entries: TarEntryInfo[], requestedPath: string): {\n prefix: string;\n execSet: Set<string>;\n} {\n if (entries.length === 0) {\n return fail(\"NOT_FOUND\", \"archive contained no entries\");\n }\n\n const prefix = entries[0].path.split(\"/\")[0];\n if (!prefix) {\n return fail(\"IO\", \"unable to determine archive prefix\");\n }\n\n const execSet = new Set<string>();\n let entryCount = 0;\n let totalSize = 0;\n let matched = false;\n\n for (const entry of entries) {\n assertSafeTarPath(entry.path);\n if (!ALLOWED_TYPES.has(entry.type)) {\n return fail(\"IO\", `unsupported entry type in archive: ${entry.path}`);\n }\n\n if (entry.path === prefix) {\n continue;\n }\n if (!entry.path.startsWith(`${prefix}/`)) {\n return fail(\"IO\", \"archive has unexpected top-level layout\");\n }\n\n const rel = entry.path.slice(prefix.length + 1);\n if (!rel) {\n continue;\n }\n\n const relToReq = stripRequestedPath(rel, requestedPath);\n if (relToReq === null) {\n continue;\n }\n\n matched = true;\n entryCount += 1;\n if (rel === requestedPath && entry.type === \"File\") {\n return fail(\"INVALID_PATH\", \"requested path is not a directory\");\n }\n if (entry.type === \"File\") {\n totalSize += entry.size ?? 0;\n if (entry.mode & 0o111) {\n execSet.add(relToReq);\n }\n }\n }\n\n if (!matched) {\n return fail(\"NOT_FOUND\", \"requested path not found in archive\");\n }\n if (entryCount > MAX_SKILLREF_FILES) {\n return fail(\"SIZE_LIMIT\", `extracted entry count ${entryCount} exceeds ${MAX_SKILLREF_FILES}`);\n }\n if (totalSize > MAX_SKILLREF_EXTRACT_BYTES) {\n return fail(\"SIZE_LIMIT\", `extracted size ${totalSize} exceeds ${MAX_SKILLREF_EXTRACT_BYTES}`);\n }\n\n return { prefix, execSet };\n}\n\nfunction stripRequestedPath(rel: string, requestedPath: string): string | null {\n if (rel === requestedPath) {\n return \"\";\n }\n const prefix = requestedPath + \"/\";\n if (rel.startsWith(prefix)) {\n return rel.slice(prefix.length);\n }\n return null;\n}\n\nfunction isUnderPath(entryPath: string, prefix: string, requestedPath: string): boolean {\n if (entryPath === prefix) {\n return false;\n }\n if (!entryPath.startsWith(`${prefix}/`)) {\n return false;\n }\n const rel = entryPath.slice(prefix.length + 1);\n if (!rel) {\n return false;\n }\n if (rel === requestedPath || rel.startsWith(requestedPath + \"/\")) {\n return true;\n }\n return false;\n}\n","import fs from \"node:fs/promises\";\nimport process from \"node:process\";\nimport { fail } from \"./errors\";\nimport { createSkillpackFromDir } from \"./skillpack\";\nimport { createSkillrefValue } from \"./skillref\";\n\nexport type SaveInput = { kind: \"string\" | \"skillpack\" | \"skillref\"; value: string };\n\nexport interface SaveOptions {\n append: boolean;\n file?: string;\n value?: string;\n dir?: string;\n url?: string;\n ref?: string;\n path?: string;\n}\n\nexport async function resolveSaveInput(\n resource: string,\n opts: SaveOptions,\n stdinIsTTY: boolean = Boolean(process.stdin.isTTY)\n): Promise<SaveInput> {\n const hasFile = typeof opts.file === \"string\";\n const hasValue = typeof opts.value === \"string\";\n const hasDir = typeof opts.dir === \"string\";\n const urlFlagsUsed = Boolean(opts.url || opts.ref || opts.path);\n const hasUrl = Boolean(opts.url && opts.path);\n const explicitCount = [hasFile, hasValue, hasDir, hasUrl].filter(Boolean).length;\n const hasStdin = !stdinIsTTY && explicitCount === 0;\n\n if (urlFlagsUsed && !hasUrl) {\n return fail(\"INVALID_INPUT\", \"--url and --path must be provided together\");\n }\n\n const methods = explicitCount + (hasStdin ? 1 : 0);\n if (methods !== 1) {\n return fail(\"INVALID_INPUT\", \"exactly one input method must be used\");\n }\n\n if (hasDir && resource !== \"skill\") {\n return fail(\"INVALID_INPUT\", \"--dir is only valid for skill save\");\n }\n if (hasUrl && resource !== \"skill\") {\n return fail(\"INVALID_INPUT\", \"--url/--ref/--path are only valid for skill save\");\n }\n if (opts.append && (hasDir || hasUrl)) {\n return fail(\"INVALID_INPUT\", \"--append cannot be used with --dir or --url\");\n }\n\n if (hasDir) {\n const value = await createSkillpackFromDir(opts.dir as string);\n return { kind: \"skillpack\", value };\n }\n\n if (hasUrl) {\n const value = createSkillrefValue(opts.url as string, opts.path as string, opts.ref as string | undefined);\n return { kind: \"skillref\", value };\n }\n\n if (hasFile) {\n const content = await fs.readFile(opts.file as string, \"utf8\");\n return { kind: \"string\", value: content };\n }\n\n if (hasValue) {\n return { kind: \"string\", value: opts.value as string };\n }\n\n const stdin = await readStdin();\n return { kind: \"string\", value: stdin };\n}\n\nasync function readStdin(): Promise<string> {\n return new Promise((resolve, reject) => {\n let data = \"\";\n process.stdin.setEncoding(\"utf8\");\n process.stdin.on(\"data\", (chunk) => {\n data += chunk;\n });\n process.stdin.on(\"end\", () => resolve(data));\n process.stdin.on(\"error\", reject);\n });\n}\n"],"mappings":"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","names":["path","import_promises","import_node_path","import_tar","import_promises","import_node_path","path","fs","fs","path","import_promises","import_node_path","fs","path","tar","fs","path","os","tar","zlib","fs","path","import_promises","import_node_path","import_node_os","import_node_fs","import_tar","fs","path","os","tar","import_promises","process","fs"]}
|
|
@@ -3,7 +3,7 @@ name: ctxbin
|
|
|
3
3
|
description: Use when working with ctxbin to save and load ctx, agent, and skill context.
|
|
4
4
|
metadata:
|
|
5
5
|
short-description: ctxbin workflow
|
|
6
|
-
version: 0.1.
|
|
6
|
+
version: 0.1.2
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# ctxbin Skill
|
|
@@ -13,6 +13,7 @@ Help agents preserve and restore **branch-scoped project context** so the next a
|
|
|
13
13
|
|
|
14
14
|
## Core Usage (ctx)
|
|
15
15
|
`ctx` automatically derives a key from the current Git repo and branch when omitted.
|
|
16
|
+
Prefer `npx ctxbin ...` when running commands in agent workflows.
|
|
16
17
|
|
|
17
18
|
```
|
|
18
19
|
key = {project}/{branch}
|
|
@@ -22,54 +23,71 @@ branch = git rev-parse --abbrev-ref HEAD
|
|
|
22
23
|
|
|
23
24
|
### Save (most common)
|
|
24
25
|
```bash
|
|
25
|
-
ctxbin ctx save --value "summary / next steps / notes"
|
|
26
|
+
npx ctxbin ctx save --value "summary / next steps / notes"
|
|
26
27
|
```
|
|
27
28
|
Or via stdin:
|
|
28
29
|
```bash
|
|
29
|
-
echo "summary" | ctxbin ctx save
|
|
30
|
+
echo "summary" | npx ctxbin ctx save
|
|
30
31
|
```
|
|
31
32
|
|
|
32
33
|
### Load
|
|
33
34
|
```bash
|
|
34
|
-
ctxbin ctx load
|
|
35
|
+
npx ctxbin ctx load
|
|
35
36
|
```
|
|
36
37
|
|
|
38
|
+
### If load returns NOT_FOUND
|
|
39
|
+
`CTXBIN_ERR NOT_FOUND: no value for ctx:<project>/<branch>` means nothing has been saved for this branch yet.
|
|
40
|
+
Tell the user and suggest:
|
|
41
|
+
- `npx ctxbin ctx save --value "<summary + next steps>"`, or
|
|
42
|
+
- Provide the current context directly.
|
|
43
|
+
|
|
44
|
+
### If load returns NOT_IN_GIT
|
|
45
|
+
`CTXBIN_ERR NOT_IN_GIT` means the command ran outside a git repository.
|
|
46
|
+
Tell the user to run it inside the project repo, or use an explicit key:
|
|
47
|
+
```bash
|
|
48
|
+
npx ctxbin ctx load <project>/<branch>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### If load returns INVALID_INPUT
|
|
52
|
+
`CTXBIN_ERR INVALID_INPUT` usually means flags were used incorrectly.
|
|
53
|
+
For `ctx load`, do not pass `--value`, `--file`, or other input flags.
|
|
54
|
+
|
|
37
55
|
### List
|
|
38
56
|
```bash
|
|
39
|
-
ctxbin ctx list
|
|
57
|
+
npx ctxbin ctx list
|
|
40
58
|
```
|
|
41
59
|
|
|
42
60
|
### Delete
|
|
43
61
|
```bash
|
|
44
|
-
ctxbin ctx delete
|
|
62
|
+
npx ctxbin ctx delete
|
|
45
63
|
```
|
|
46
64
|
|
|
47
65
|
## agent Save/Load
|
|
48
66
|
`agent` requires a key and stores **string values only**.
|
|
49
67
|
|
|
50
68
|
```bash
|
|
51
|
-
ctxbin agent save reviewer --value "# Agent role"
|
|
52
|
-
ctxbin agent load reviewer
|
|
69
|
+
npx ctxbin agent save reviewer --value "# Agent role"
|
|
70
|
+
npx ctxbin agent load reviewer
|
|
53
71
|
```
|
|
54
72
|
|
|
55
73
|
### List/Delete
|
|
56
74
|
```bash
|
|
57
|
-
ctxbin agent list
|
|
58
|
-
ctxbin agent delete reviewer
|
|
75
|
+
npx ctxbin agent list
|
|
76
|
+
npx ctxbin agent delete reviewer
|
|
59
77
|
```
|
|
60
78
|
|
|
61
79
|
## skill Save/Load
|
|
62
80
|
`skill` requires a key.
|
|
63
81
|
|
|
64
82
|
```bash
|
|
65
|
-
ctxbin skill save my-skill --value "# Skill markdown"
|
|
66
|
-
ctxbin skill load my-skill
|
|
83
|
+
npx ctxbin skill save my-skill --value "# Skill markdown"
|
|
84
|
+
npx ctxbin skill load my-skill
|
|
67
85
|
```
|
|
68
86
|
|
|
69
87
|
### List/Delete
|
|
70
88
|
```bash
|
|
71
|
-
ctxbin skill list
|
|
72
|
-
ctxbin skill delete my-skill
|
|
89
|
+
npx ctxbin skill list
|
|
90
|
+
npx ctxbin skill delete my-skill
|
|
73
91
|
```
|
|
74
92
|
|
|
75
93
|
## Input Options (`--file`, `--value`, `--dir`, `--url`)
|
|
@@ -77,34 +95,34 @@ Use **exactly one** input method.
|
|
|
77
95
|
|
|
78
96
|
- `--value`: store a literal string
|
|
79
97
|
```bash
|
|
80
|
-
ctxbin ctx save --value "summary"
|
|
81
|
-
ctxbin agent save reviewer --value "# Agent role"
|
|
82
|
-
ctxbin skill save my-skill --value "# Skill markdown"
|
|
98
|
+
npx ctxbin ctx save --value "summary"
|
|
99
|
+
npx ctxbin agent save reviewer --value "# Agent role"
|
|
100
|
+
npx ctxbin skill save my-skill --value "# Skill markdown"
|
|
83
101
|
```
|
|
84
102
|
|
|
85
103
|
- `--file`: store file contents
|
|
86
104
|
```bash
|
|
87
|
-
ctxbin ctx save --file context.md
|
|
88
|
-
ctxbin agent save reviewer --file agent.md
|
|
89
|
-
ctxbin skill save my-skill --file SKILL.md
|
|
105
|
+
npx ctxbin ctx save --file context.md
|
|
106
|
+
npx ctxbin agent save reviewer --file agent.md
|
|
107
|
+
npx ctxbin skill save my-skill --file SKILL.md
|
|
90
108
|
```
|
|
91
109
|
|
|
92
110
|
- `--dir`: store a directory as a skillpack (skill-only)
|
|
93
111
|
```bash
|
|
94
|
-
ctxbin skill save my-skill --dir ./skills/my-skill
|
|
95
|
-
ctxbin skill load my-skill --dir ./tmp/my-skill
|
|
112
|
+
npx ctxbin skill save my-skill --dir ./skills/my-skill
|
|
113
|
+
npx ctxbin skill load my-skill --dir ./tmp/my-skill
|
|
96
114
|
```
|
|
97
115
|
|
|
98
116
|
- `--url` (+ `--path`, optional `--ref`): GitHub directory reference (skill-only)
|
|
99
117
|
```bash
|
|
100
118
|
# Pin to a specific commit
|
|
101
|
-
ctxbin skill save my-skill \
|
|
119
|
+
npx ctxbin skill save my-skill \
|
|
102
120
|
--url https://github.com/OWNER/REPO \
|
|
103
121
|
--ref <40-hex-commit-sha> \
|
|
104
122
|
--path skills/my-skill
|
|
105
123
|
|
|
106
124
|
# Track default branch (omit --ref)
|
|
107
|
-
ctxbin skill save my-skill \
|
|
125
|
+
npx ctxbin skill save my-skill \
|
|
108
126
|
--url https://github.com/OWNER/REPO \
|
|
109
127
|
--path skills/my-skill
|
|
110
128
|
```
|
|
@@ -113,9 +131,9 @@ Use **exactly one** input method.
|
|
|
113
131
|
`--append` works with **string inputs only**.
|
|
114
132
|
|
|
115
133
|
```bash
|
|
116
|
-
ctxbin ctx save --append --value "more notes"
|
|
117
|
-
ctxbin agent save reviewer --append --value "extra role details"
|
|
118
|
-
ctxbin skill save my-skill --append --value "extra string"
|
|
134
|
+
npx ctxbin ctx save --append --value "more notes"
|
|
135
|
+
npx ctxbin agent save reviewer --append --value "extra role details"
|
|
136
|
+
npx ctxbin skill save my-skill --append --value "extra string"
|
|
119
137
|
```
|
|
120
138
|
|
|
121
139
|
## What agents must include in ctx
|
|
@@ -134,4 +152,3 @@ Context is stored in Upstash Redis hash `ctx` under field `{project}/{branch}`.
|
|
|
134
152
|
## Do Not
|
|
135
153
|
- Don’t store secrets
|
|
136
154
|
- Don’t overwrite with trivial messages
|
|
137
|
-
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ctxbin",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Minimal deterministic CLI to store/load context, agents, and skills via Redis hashes",
|
|
5
5
|
"author": "superlucky84",
|
|
6
6
|
"license": "MIT",
|
|
@@ -44,6 +44,9 @@
|
|
|
44
44
|
},
|
|
45
45
|
"scripts": {
|
|
46
46
|
"build": "tsup && node scripts/copy-skills.js",
|
|
47
|
-
"test": "pnpm build && node --test \"tests/**/*.test.js\""
|
|
47
|
+
"test": "pnpm build && node --test \"tests/**/*.test.js\"",
|
|
48
|
+
"clean": "pnpm clean:dist && pnpm clean:module",
|
|
49
|
+
"clean:dist": "rm -rf dist docs/dist",
|
|
50
|
+
"clean:module": "rm -rf dist docs/dist node_modules"
|
|
48
51
|
}
|
|
49
52
|
}
|
package/skills/ctxbin/SKILL.md
CHANGED
|
@@ -5,6 +5,7 @@ Help agents preserve and restore **branch-scoped project context** so the next a
|
|
|
5
5
|
|
|
6
6
|
## Core Usage (ctx)
|
|
7
7
|
`ctx` automatically derives a key from the current Git repo and branch when omitted.
|
|
8
|
+
Prefer `npx ctxbin ...` when running commands in agent workflows.
|
|
8
9
|
|
|
9
10
|
```
|
|
10
11
|
key = {project}/{branch}
|
|
@@ -14,54 +15,71 @@ branch = git rev-parse --abbrev-ref HEAD
|
|
|
14
15
|
|
|
15
16
|
### Save (most common)
|
|
16
17
|
```bash
|
|
17
|
-
ctxbin ctx save --value "summary / next steps / notes"
|
|
18
|
+
npx ctxbin ctx save --value "summary / next steps / notes"
|
|
18
19
|
```
|
|
19
20
|
Or via stdin:
|
|
20
21
|
```bash
|
|
21
|
-
echo "summary" | ctxbin ctx save
|
|
22
|
+
echo "summary" | npx ctxbin ctx save
|
|
22
23
|
```
|
|
23
24
|
|
|
24
25
|
### Load
|
|
25
26
|
```bash
|
|
26
|
-
ctxbin ctx load
|
|
27
|
+
npx ctxbin ctx load
|
|
27
28
|
```
|
|
28
29
|
|
|
30
|
+
### If load returns NOT_FOUND
|
|
31
|
+
`CTXBIN_ERR NOT_FOUND: no value for ctx:<project>/<branch>` means nothing has been saved for this branch yet.
|
|
32
|
+
Tell the user and suggest:
|
|
33
|
+
- `npx ctxbin ctx save --value "<summary + next steps>"`, or
|
|
34
|
+
- Provide the current context directly.
|
|
35
|
+
|
|
36
|
+
### If load returns NOT_IN_GIT
|
|
37
|
+
`CTXBIN_ERR NOT_IN_GIT` means the command ran outside a git repository.
|
|
38
|
+
Tell the user to run it inside the project repo, or use an explicit key:
|
|
39
|
+
```bash
|
|
40
|
+
npx ctxbin ctx load <project>/<branch>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### If load returns INVALID_INPUT
|
|
44
|
+
`CTXBIN_ERR INVALID_INPUT` usually means flags were used incorrectly.
|
|
45
|
+
For `ctx load`, do not pass `--value`, `--file`, or other input flags.
|
|
46
|
+
|
|
29
47
|
### List
|
|
30
48
|
```bash
|
|
31
|
-
ctxbin ctx list
|
|
49
|
+
npx ctxbin ctx list
|
|
32
50
|
```
|
|
33
51
|
|
|
34
52
|
### Delete
|
|
35
53
|
```bash
|
|
36
|
-
ctxbin ctx delete
|
|
54
|
+
npx ctxbin ctx delete
|
|
37
55
|
```
|
|
38
56
|
|
|
39
57
|
## agent Save/Load
|
|
40
58
|
`agent` requires a key and stores **string values only**.
|
|
41
59
|
|
|
42
60
|
```bash
|
|
43
|
-
ctxbin agent save reviewer --value "# Agent role"
|
|
44
|
-
ctxbin agent load reviewer
|
|
61
|
+
npx ctxbin agent save reviewer --value "# Agent role"
|
|
62
|
+
npx ctxbin agent load reviewer
|
|
45
63
|
```
|
|
46
64
|
|
|
47
65
|
### List/Delete
|
|
48
66
|
```bash
|
|
49
|
-
ctxbin agent list
|
|
50
|
-
ctxbin agent delete reviewer
|
|
67
|
+
npx ctxbin agent list
|
|
68
|
+
npx ctxbin agent delete reviewer
|
|
51
69
|
```
|
|
52
70
|
|
|
53
71
|
## skill Save/Load
|
|
54
72
|
`skill` requires a key.
|
|
55
73
|
|
|
56
74
|
```bash
|
|
57
|
-
ctxbin skill save my-skill --value "# Skill markdown"
|
|
58
|
-
ctxbin skill load my-skill
|
|
75
|
+
npx ctxbin skill save my-skill --value "# Skill markdown"
|
|
76
|
+
npx ctxbin skill load my-skill
|
|
59
77
|
```
|
|
60
78
|
|
|
61
79
|
### List/Delete
|
|
62
80
|
```bash
|
|
63
|
-
ctxbin skill list
|
|
64
|
-
ctxbin skill delete my-skill
|
|
81
|
+
npx ctxbin skill list
|
|
82
|
+
npx ctxbin skill delete my-skill
|
|
65
83
|
```
|
|
66
84
|
|
|
67
85
|
## Input Options (`--file`, `--value`, `--dir`, `--url`)
|
|
@@ -69,34 +87,34 @@ Use **exactly one** input method.
|
|
|
69
87
|
|
|
70
88
|
- `--value`: store a literal string
|
|
71
89
|
```bash
|
|
72
|
-
ctxbin ctx save --value "summary"
|
|
73
|
-
ctxbin agent save reviewer --value "# Agent role"
|
|
74
|
-
ctxbin skill save my-skill --value "# Skill markdown"
|
|
90
|
+
npx ctxbin ctx save --value "summary"
|
|
91
|
+
npx ctxbin agent save reviewer --value "# Agent role"
|
|
92
|
+
npx ctxbin skill save my-skill --value "# Skill markdown"
|
|
75
93
|
```
|
|
76
94
|
|
|
77
95
|
- `--file`: store file contents
|
|
78
96
|
```bash
|
|
79
|
-
ctxbin ctx save --file context.md
|
|
80
|
-
ctxbin agent save reviewer --file agent.md
|
|
81
|
-
ctxbin skill save my-skill --file SKILL.md
|
|
97
|
+
npx ctxbin ctx save --file context.md
|
|
98
|
+
npx ctxbin agent save reviewer --file agent.md
|
|
99
|
+
npx ctxbin skill save my-skill --file SKILL.md
|
|
82
100
|
```
|
|
83
101
|
|
|
84
102
|
- `--dir`: store a directory as a skillpack (skill-only)
|
|
85
103
|
```bash
|
|
86
|
-
ctxbin skill save my-skill --dir ./skills/my-skill
|
|
87
|
-
ctxbin skill load my-skill --dir ./tmp/my-skill
|
|
104
|
+
npx ctxbin skill save my-skill --dir ./skills/my-skill
|
|
105
|
+
npx ctxbin skill load my-skill --dir ./tmp/my-skill
|
|
88
106
|
```
|
|
89
107
|
|
|
90
108
|
- `--url` (+ `--path`, optional `--ref`): GitHub directory reference (skill-only)
|
|
91
109
|
```bash
|
|
92
110
|
# Pin to a specific commit
|
|
93
|
-
ctxbin skill save my-skill \
|
|
111
|
+
npx ctxbin skill save my-skill \
|
|
94
112
|
--url https://github.com/OWNER/REPO \
|
|
95
113
|
--ref <40-hex-commit-sha> \
|
|
96
114
|
--path skills/my-skill
|
|
97
115
|
|
|
98
116
|
# Track default branch (omit --ref)
|
|
99
|
-
ctxbin skill save my-skill \
|
|
117
|
+
npx ctxbin skill save my-skill \
|
|
100
118
|
--url https://github.com/OWNER/REPO \
|
|
101
119
|
--path skills/my-skill
|
|
102
120
|
```
|
|
@@ -105,9 +123,9 @@ Use **exactly one** input method.
|
|
|
105
123
|
`--append` works with **string inputs only**.
|
|
106
124
|
|
|
107
125
|
```bash
|
|
108
|
-
ctxbin ctx save --append --value "more notes"
|
|
109
|
-
ctxbin agent save reviewer --append --value "extra role details"
|
|
110
|
-
ctxbin skill save my-skill --append --value "extra string"
|
|
126
|
+
npx ctxbin ctx save --append --value "more notes"
|
|
127
|
+
npx ctxbin agent save reviewer --append --value "extra role details"
|
|
128
|
+
npx ctxbin skill save my-skill --append --value "extra string"
|
|
111
129
|
```
|
|
112
130
|
|
|
113
131
|
## What agents must include in ctx
|
|
@@ -126,4 +144,3 @@ Context is stored in Upstash Redis hash `ctx` under field `{project}/{branch}`.
|
|
|
126
144
|
## Do Not
|
|
127
145
|
- Don’t store secrets
|
|
128
146
|
- Don’t overwrite with trivial messages
|
|
129
|
-
|