@jenz-ai/sdk 0.1.1 → 0.2.1
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 +11 -0
- package/SETUP.md +272 -0
- package/dist/cli.js +373 -0
- package/dist/cli.js.map +1 -0
- package/package.json +10 -11
package/README.md
CHANGED
|
@@ -12,6 +12,17 @@ pnpm add @jenz-ai/sdk
|
|
|
12
12
|
yarn add @jenz-ai/sdk
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
+
## Quickstart (AI-guided)
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npx @jenz-ai/sdk init
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Detects which AI coding tool you have (Claude Code, Cursor,
|
|
22
|
+
Codex, AGENTS.md) and installs a setup skill that walks you
|
|
23
|
+
through wiring up the SDK in your agent — including prompting
|
|
24
|
+
for an API key.
|
|
25
|
+
|
|
15
26
|
## Usage
|
|
16
27
|
|
|
17
28
|
```ts
|
package/SETUP.md
ADDED
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
# Jenz Setup
|
|
2
|
+
|
|
3
|
+
You are setting up Jenz observability for the user's AI agent.
|
|
4
|
+
Follow these steps strictly in order. Do not skip steps. Do not
|
|
5
|
+
do them out of order.
|
|
6
|
+
|
|
7
|
+
## What Jenz is
|
|
8
|
+
|
|
9
|
+
Jenz (https://jenz.dev) is observability for AI agents. It tracks:
|
|
10
|
+
|
|
11
|
+
- **Runs** — one execution of an agent (e.g. one cron firing, one
|
|
12
|
+
task, one chat turn).
|
|
13
|
+
- **Events** under each run — typed: `llm_call`, `tool_call`, or
|
|
14
|
+
`log`. LLM calls capture tokens; cost is auto-computed from
|
|
15
|
+
`model + inputTokens + outputTokens`.
|
|
16
|
+
- The dashboard at https://jenz.dev shows latency, cost per run,
|
|
17
|
+
errors, and the full event tree.
|
|
18
|
+
|
|
19
|
+
The SDK (`@jenz-ai/sdk`) is fire-and-forget: HTTP errors never
|
|
20
|
+
throw. `Jenz.startRun` returns `null` if the backend is
|
|
21
|
+
unreachable so the host process keeps running.
|
|
22
|
+
|
|
23
|
+
## Use the best tool you have
|
|
24
|
+
|
|
25
|
+
- If you have a structured-choice tool (e.g. `AskUserQuestion` in
|
|
26
|
+
Claude Code), use it for Step 3 instead of free-text questions.
|
|
27
|
+
- If you have a todo-tracker (e.g. `TodoWrite` in Claude Code),
|
|
28
|
+
create one todo per step below and mark them as you go.
|
|
29
|
+
- Otherwise, ask in plain text. Be concise.
|
|
30
|
+
|
|
31
|
+
## Step 1 — Verify install
|
|
32
|
+
|
|
33
|
+
Read `package.json` in the project root. If `@jenz-ai/sdk` is not
|
|
34
|
+
in `dependencies`, install it:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pnpm add @jenz-ai/sdk # or npm install / yarn add
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Step 2 — Map the agent
|
|
41
|
+
|
|
42
|
+
Read the project structure. Identify and report back to the user:
|
|
43
|
+
|
|
44
|
+
- The agent's entry/loop function (the function called per task,
|
|
45
|
+
per cron firing, per chat turn).
|
|
46
|
+
- LLM call sites — search for: `anthropic`, `openai`, `@ai-sdk/`,
|
|
47
|
+
`ollama`, `groq`, `together`, `mistral`. Note the model names
|
|
48
|
+
you find.
|
|
49
|
+
- Tool / sub-step boundaries — function calls that the agent
|
|
50
|
+
delegates work to (search, file IO, API calls).
|
|
51
|
+
|
|
52
|
+
Report a short summary like:
|
|
53
|
+
|
|
54
|
+
> Found:
|
|
55
|
+
> - Entry: `src/agent.ts:runAgent()`
|
|
56
|
+
> - LLM calls: 2 (anthropic claude-haiku-4-5 in agent.ts:42,
|
|
57
|
+
> openai gpt-4o in tools.ts:18)
|
|
58
|
+
> - Tools: `searchWeb()`, `summarize()`
|
|
59
|
+
|
|
60
|
+
## Step 3 — Ask 3 questions
|
|
61
|
+
|
|
62
|
+
Ask one at a time:
|
|
63
|
+
|
|
64
|
+
1. **agentName** — "What's a stable, kebab-case identifier for
|
|
65
|
+
this agent? (e.g. `seo-agent`, `support-bot`)"
|
|
66
|
+
2. **agentType** — "Which type fits best: `scheduled` (cron /
|
|
67
|
+
queue), `claude_code` (runs inside an editor), or `manual`
|
|
68
|
+
(CLI / one-off)?"
|
|
69
|
+
3. **Granularity** — "Wrap every LLM call as its own event so you
|
|
70
|
+
can see per-call cost and latency? (recommended) Or only
|
|
71
|
+
track the top-level run?"
|
|
72
|
+
|
|
73
|
+
If they say yes to per-LLM-call wrapping, also ask:
|
|
74
|
+
4. **Tools** — "Wrap tool / sub-function calls as events too?"
|
|
75
|
+
|
|
76
|
+
## Step 4 — Make the code changes
|
|
77
|
+
|
|
78
|
+
Based on the answers:
|
|
79
|
+
|
|
80
|
+
- Import and initialize `Jenz` once at module top:
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
import { Jenz } from '@jenz-ai/sdk';
|
|
84
|
+
|
|
85
|
+
const jenz = new Jenz({
|
|
86
|
+
apiKey: process.env.JENZ_API_KEY!,
|
|
87
|
+
agentName: '<agentName from Step 3>',
|
|
88
|
+
agentType: '<agentType from Step 3>',
|
|
89
|
+
});
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
- Wrap the entry function with `startRun` / `run.finish`. Use
|
|
93
|
+
try/catch so errors are recorded:
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
async function runAgent(input: string) {
|
|
97
|
+
const run = await jenz.startRun({ input });
|
|
98
|
+
if (!run) {
|
|
99
|
+
// backend unreachable — proceed without observability
|
|
100
|
+
return doWork(input);
|
|
101
|
+
}
|
|
102
|
+
try {
|
|
103
|
+
const output = await doWork(input);
|
|
104
|
+
await run.finish({ status: 'completed', output });
|
|
105
|
+
return output;
|
|
106
|
+
} catch (err) {
|
|
107
|
+
await run.finish({
|
|
108
|
+
status: 'errored',
|
|
109
|
+
errorMessage: err instanceof Error ? err.message : String(err),
|
|
110
|
+
});
|
|
111
|
+
throw err;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
- If the user said yes to per-LLM-call wrapping, wrap each LLM
|
|
117
|
+
call:
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
const evt = run.startEvent({
|
|
121
|
+
type: 'llm_call',
|
|
122
|
+
model: 'claude-haiku-4-5',
|
|
123
|
+
input: prompt,
|
|
124
|
+
});
|
|
125
|
+
const res = await callLlm(prompt);
|
|
126
|
+
await evt.finish({
|
|
127
|
+
output: res.text,
|
|
128
|
+
inputTokens: res.usage.input_tokens,
|
|
129
|
+
outputTokens: res.usage.output_tokens,
|
|
130
|
+
});
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
**Important:**
|
|
134
|
+
- Await every `evt.finish()` BEFORE `run.finish()` — the backend
|
|
135
|
+
stamps the run's `endedAt` on the finish PATCH, so events that
|
|
136
|
+
arrive after will be orphaned.
|
|
137
|
+
- If an LLM call throws between `startEvent` and `evt.finish`,
|
|
138
|
+
call `evt.finish({ errorMessage: ... })` before re-throwing —
|
|
139
|
+
otherwise the event is orphaned with no end time and no error.
|
|
140
|
+
|
|
141
|
+
- If the user said yes to wrapping tools, wrap them with
|
|
142
|
+
`type: 'tool_call'` the same way (no token fields needed).
|
|
143
|
+
|
|
144
|
+
Show a diff of the changes before writing them and let the user
|
|
145
|
+
approve.
|
|
146
|
+
|
|
147
|
+
## Step 5 — API key
|
|
148
|
+
|
|
149
|
+
Check the project's `.env`, `.env.local`, and `.env.development`
|
|
150
|
+
for an existing `JENZ_API_KEY`. If one is set, skip this step
|
|
151
|
+
and tell the user "Existing key found, skipping".
|
|
152
|
+
|
|
153
|
+
Otherwise, tell the user verbatim:
|
|
154
|
+
|
|
155
|
+
> You need a Jenz API key. Get one here:
|
|
156
|
+
> **https://jenz.dev/api-keys**
|
|
157
|
+
>
|
|
158
|
+
> If you don't have an account, sign in with email at the same
|
|
159
|
+
> URL — it takes about 30 seconds. Then click "New key" and
|
|
160
|
+
> paste the value back here.
|
|
161
|
+
|
|
162
|
+
When the user pastes the key:
|
|
163
|
+
|
|
164
|
+
1. If `.env` does not exist, create it with `JENZ_API_KEY=<key>`.
|
|
165
|
+
If it exists and already contains a `JENZ_API_KEY=` line,
|
|
166
|
+
replace that line in place. Otherwise, append the new line.
|
|
167
|
+
2. Ensure `.env` is in `.gitignore`. If `.gitignore` does not
|
|
168
|
+
exist or does not include `.env`, append it.
|
|
169
|
+
3. Do not echo the key value back in chat output.
|
|
170
|
+
|
|
171
|
+
## Step 6 — Verify
|
|
172
|
+
|
|
173
|
+
Write and run a 5-line smoke test that proves auth works.
|
|
174
|
+
|
|
175
|
+
If the project uses a TypeScript-aware runtime (`bun`, `deno`, `tsx`, or Node 22.6+), use `.ts`:
|
|
176
|
+
|
|
177
|
+
```ts
|
|
178
|
+
import { Jenz } from '@jenz-ai/sdk';
|
|
179
|
+
|
|
180
|
+
const j = new Jenz({
|
|
181
|
+
apiKey: process.env.JENZ_API_KEY!,
|
|
182
|
+
agentName: 'cli-smoke',
|
|
183
|
+
agentType: 'manual',
|
|
184
|
+
});
|
|
185
|
+
const r = await j.startRun({ input: 'CLI setup verification' });
|
|
186
|
+
if (!r) {
|
|
187
|
+
console.error('✗ Key invalid or backend unreachable');
|
|
188
|
+
process.exit(1);
|
|
189
|
+
}
|
|
190
|
+
await r.finish({ status: 'completed', output: 'ok' });
|
|
191
|
+
console.log('✓ Verified — check https://jenz.dev/');
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Otherwise (plain Node 20.6+), save it as `smoke.mjs` (no type annotations needed):
|
|
195
|
+
|
|
196
|
+
```js
|
|
197
|
+
import { Jenz } from '@jenz-ai/sdk';
|
|
198
|
+
|
|
199
|
+
const j = new Jenz({
|
|
200
|
+
apiKey: process.env.JENZ_API_KEY,
|
|
201
|
+
agentName: 'cli-smoke',
|
|
202
|
+
agentType: 'manual',
|
|
203
|
+
});
|
|
204
|
+
const r = await j.startRun({ input: 'CLI setup verification' });
|
|
205
|
+
if (!r) {
|
|
206
|
+
console.error('✗ Key invalid or backend unreachable');
|
|
207
|
+
process.exit(1);
|
|
208
|
+
}
|
|
209
|
+
await r.finish({ status: 'completed', output: 'ok' });
|
|
210
|
+
console.log('✓ Verified — check https://jenz.dev/');
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Run it with the user's runtime:
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
node --env-file=.env smoke.mjs # Node 20.6+
|
|
217
|
+
bun run --env-file=.env smoke.ts # Bun
|
|
218
|
+
tsx --env-file=.env smoke.ts # tsx
|
|
219
|
+
deno run --env-file=.env --allow-all smoke.ts # Deno
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
If the smoke test passes, tell the user:
|
|
223
|
+
|
|
224
|
+
> Setup complete. Run your agent — runs will appear at
|
|
225
|
+
> https://jenz.dev/ within a few seconds.
|
|
226
|
+
|
|
227
|
+
If `startRun` returned `null`, the API key is invalid or the
|
|
228
|
+
backend is unreachable. Tell the user to double-check the key.
|
|
229
|
+
|
|
230
|
+
## SDK API reference
|
|
231
|
+
|
|
232
|
+
```ts
|
|
233
|
+
import { Jenz } from '@jenz-ai/sdk';
|
|
234
|
+
|
|
235
|
+
const jenz = new Jenz({
|
|
236
|
+
apiKey: string, // required
|
|
237
|
+
agentName: string, // required, stable identifier
|
|
238
|
+
agentType: 'scheduled' | 'claude_code' | 'manual',
|
|
239
|
+
// baseUrl is optional and defaults to https://api.jenz.dev
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
const run = await jenz.startRun({
|
|
243
|
+
input?: string,
|
|
244
|
+
expectedDurationMs?: number,
|
|
245
|
+
metadata?: Record<string, unknown>,
|
|
246
|
+
});
|
|
247
|
+
// returns Run | null (null on backend unreachable)
|
|
248
|
+
|
|
249
|
+
const evt = run.startEvent({
|
|
250
|
+
type: 'llm_call' | 'tool_call' | 'log',
|
|
251
|
+
model?: string,
|
|
252
|
+
input?: string,
|
|
253
|
+
parentEventId?: string,
|
|
254
|
+
metadata?: Record<string, unknown>,
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
await evt.finish({
|
|
258
|
+
output?: string,
|
|
259
|
+
inputTokens?: number,
|
|
260
|
+
outputTokens?: number,
|
|
261
|
+
costUsd?: number, // omit to let backend compute
|
|
262
|
+
errorMessage?: string,
|
|
263
|
+
metadata?: Record<string, unknown>,
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
await run.finish({
|
|
267
|
+
status: 'completed' | 'errored' | 'stopped',
|
|
268
|
+
output?: string,
|
|
269
|
+
errorMessage?: string,
|
|
270
|
+
metadata?: Record<string, unknown>,
|
|
271
|
+
});
|
|
272
|
+
```
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/cli/init.ts
|
|
4
|
+
import { intro, multiselect, note, outro, isCancel, cancel, select, log } from "@clack/prompts";
|
|
5
|
+
import pc from "picocolors";
|
|
6
|
+
|
|
7
|
+
// src/cli/logo.ts
|
|
8
|
+
var LOGO = ` \u2591\u2588\u2588\u2588\u2588\u2588
|
|
9
|
+
\u2591\u2588\u2588
|
|
10
|
+
\u2591\u2588\u2588 \u2591\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2591\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2591\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588
|
|
11
|
+
\u2591\u2588\u2588 \u2591\u2588\u2588 \u2591\u2588\u2588 \u2591\u2588\u2588 \u2591\u2588\u2588 \u2591\u2588\u2588\u2588
|
|
12
|
+
\u2591\u2588\u2588 \u2591\u2588\u2588 \u2591\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2591\u2588\u2588 \u2591\u2588\u2588 \u2591\u2588\u2588\u2588
|
|
13
|
+
\u2591\u2588\u2588 \u2591\u2588\u2588 \u2591\u2588\u2588 \u2591\u2588\u2588 \u2591\u2588\u2588 \u2591\u2588\u2588\u2588
|
|
14
|
+
\u2591\u2588\u2588\u2588\u2588\u2588\u2588 \u2591\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2591\u2588\u2588 \u2591\u2588\u2588 \u2591\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2591\u2588\u2588 \u2591\u2588\u2588 \u2591\u2588\u2588 \u2591\u2588\u2588
|
|
15
|
+
`;
|
|
16
|
+
var TAGLINE = "observability for AI agents";
|
|
17
|
+
|
|
18
|
+
// src/cli/detect.ts
|
|
19
|
+
import { existsSync } from "fs";
|
|
20
|
+
import { join } from "path";
|
|
21
|
+
import { homedir } from "os";
|
|
22
|
+
var PATH_SEP = process.platform === "win32" ? ";" : ":";
|
|
23
|
+
function isBinaryInPath(binary, pathEnv) {
|
|
24
|
+
if (!pathEnv) return false;
|
|
25
|
+
for (const dir of pathEnv.split(PATH_SEP)) {
|
|
26
|
+
if (!dir) continue;
|
|
27
|
+
if (existsSync(join(dir, binary))) return true;
|
|
28
|
+
}
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
function detectTools(inputs) {
|
|
32
|
+
const home = inputs?.homedir ?? homedir();
|
|
33
|
+
const cwd = inputs?.cwd ?? process.cwd();
|
|
34
|
+
const pathEnv = inputs?.path ?? process.env.PATH ?? "";
|
|
35
|
+
return {
|
|
36
|
+
claude: existsSync(join(home, ".claude")),
|
|
37
|
+
cursor: existsSync(join(cwd, ".cursor")) || existsSync(join(home, ".cursor")),
|
|
38
|
+
codex: existsSync(join(home, ".codex")) || isBinaryInPath("codex", pathEnv),
|
|
39
|
+
agents: true
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// src/cli/skill-content.ts
|
|
44
|
+
import { readFileSync } from "fs";
|
|
45
|
+
import { fileURLToPath } from "url";
|
|
46
|
+
import { dirname, join as join2 } from "path";
|
|
47
|
+
function findSetupMd() {
|
|
48
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
49
|
+
const candidates = [
|
|
50
|
+
join2(here, "..", "SETUP.md"),
|
|
51
|
+
join2(here, "..", "..", "SETUP.md"),
|
|
52
|
+
join2(here, "..", "..", "..", "SETUP.md")
|
|
53
|
+
];
|
|
54
|
+
for (const p of candidates) {
|
|
55
|
+
try {
|
|
56
|
+
return readFileSync(p, "utf8");
|
|
57
|
+
} catch {
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
throw new Error(
|
|
61
|
+
`[jenz] could not locate SETUP.md (tried: ${candidates.join(", ")})`
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
var cached;
|
|
65
|
+
function readSkillBody() {
|
|
66
|
+
if (cached === void 0) cached = findSetupMd();
|
|
67
|
+
return cached;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// src/cli/install/claude.ts
|
|
71
|
+
import { writeFileSync, mkdirSync, existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
72
|
+
import { join as join3, dirname as dirname2 } from "path";
|
|
73
|
+
import { homedir as homedir2 } from "os";
|
|
74
|
+
|
|
75
|
+
// src/cli/install/conflict.ts
|
|
76
|
+
function renamedPath(filePath) {
|
|
77
|
+
const dotIdx = filePath.lastIndexOf(".");
|
|
78
|
+
const slashIdx = Math.max(filePath.lastIndexOf("/"), filePath.lastIndexOf("\\"));
|
|
79
|
+
if (dotIdx <= slashIdx) return `${filePath}.new`;
|
|
80
|
+
return `${filePath.slice(0, dotIdx)}.new${filePath.slice(dotIdx)}`;
|
|
81
|
+
}
|
|
82
|
+
function diffHint(existing, planned) {
|
|
83
|
+
const exLines = existing.split("\n");
|
|
84
|
+
const plLines = planned.split("\n");
|
|
85
|
+
let firstDiff = -1;
|
|
86
|
+
const max = Math.max(exLines.length, plLines.length);
|
|
87
|
+
for (let i = 0; i < max; i++) {
|
|
88
|
+
if (exLines[i] !== plLines[i]) {
|
|
89
|
+
firstDiff = i;
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (firstDiff === -1) return "Files match (no diff).";
|
|
94
|
+
const exLine = exLines[firstDiff] ?? "(end of file)";
|
|
95
|
+
const plLine = plLines[firstDiff] ?? "(end of file)";
|
|
96
|
+
const truncated = (s) => s.length > 80 ? `${s.slice(0, 77)}...` : s;
|
|
97
|
+
return [
|
|
98
|
+
`existing ${exLines.length} lines, new ${plLines.length} lines (first diff at line ${firstDiff + 1})`,
|
|
99
|
+
` - ${truncated(exLine)}`,
|
|
100
|
+
` + ${truncated(plLine)}`
|
|
101
|
+
].join("\n");
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// src/cli/install/claude.ts
|
|
105
|
+
var SKILL_FRONTMATTER = `---
|
|
106
|
+
name: jenz-setup
|
|
107
|
+
description: Set up Jenz observability (jenz.dev) for an AI agent \u2014 detects the agent's main loop and LLM calls, asks about logging scope, wires up @jenz-ai/sdk, and prompts for an API key. Use when the user says "set up jenz", "add jenz", "monitor my agent with jenz", or just installed @jenz-ai/sdk.
|
|
108
|
+
---
|
|
109
|
+
`;
|
|
110
|
+
var SLASH_COMMAND = `---
|
|
111
|
+
description: Set up Jenz observability for this project
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
Activate the jenz-setup skill and walk me through wiring up Jenz
|
|
115
|
+
in this project. Use AskUserQuestion for the configuration
|
|
116
|
+
questions and TodoWrite to track the 6 setup steps.
|
|
117
|
+
`;
|
|
118
|
+
async function writeWithConflictPolicy(filePath, planned, onConflict) {
|
|
119
|
+
mkdirSync(dirname2(filePath), { recursive: true });
|
|
120
|
+
if (existsSync2(filePath) && onConflict) {
|
|
121
|
+
const existing = readFileSync2(filePath, "utf8");
|
|
122
|
+
if (existing !== planned) {
|
|
123
|
+
const decision = await onConflict(filePath, existing, planned);
|
|
124
|
+
if (decision === "skip") return null;
|
|
125
|
+
if (decision === "rename") {
|
|
126
|
+
const newPath = renamedPath(filePath);
|
|
127
|
+
writeFileSync(newPath, planned, "utf8");
|
|
128
|
+
return newPath;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
writeFileSync(filePath, planned, "utf8");
|
|
133
|
+
return filePath;
|
|
134
|
+
}
|
|
135
|
+
async function installClaude(skillBody, opts = {}) {
|
|
136
|
+
const home = opts.homedir ?? homedir2();
|
|
137
|
+
const skillPath = join3(home, ".claude", "skills", "jenz-setup", "SKILL.md");
|
|
138
|
+
const cmdPath = join3(home, ".claude", "commands", "jenz.md");
|
|
139
|
+
const written = [];
|
|
140
|
+
const skillResult = await writeWithConflictPolicy(
|
|
141
|
+
skillPath,
|
|
142
|
+
`${SKILL_FRONTMATTER}
|
|
143
|
+
${skillBody}`,
|
|
144
|
+
opts.onConflict
|
|
145
|
+
);
|
|
146
|
+
if (skillResult) written.push(skillResult);
|
|
147
|
+
const cmdResult = await writeWithConflictPolicy(
|
|
148
|
+
cmdPath,
|
|
149
|
+
SLASH_COMMAND,
|
|
150
|
+
opts.onConflict
|
|
151
|
+
);
|
|
152
|
+
if (cmdResult) written.push(cmdResult);
|
|
153
|
+
return { written };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// src/cli/install/cursor.ts
|
|
157
|
+
import { writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
|
|
158
|
+
import { join as join4, dirname as dirname3 } from "path";
|
|
159
|
+
var FRONTMATTER = `---
|
|
160
|
+
description: Jenz observability setup playbook \u2014 wires up @jenz-ai/sdk in this project. Activate when the user asks to set up jenz, add observability, or track agent runs.
|
|
161
|
+
alwaysApply: false
|
|
162
|
+
---
|
|
163
|
+
`;
|
|
164
|
+
async function installCursor(skillBody, opts = {}) {
|
|
165
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
166
|
+
const filePath = join4(cwd, ".cursor", "rules", "jenz-setup.mdc");
|
|
167
|
+
const planned = `${FRONTMATTER}
|
|
168
|
+
${skillBody}`;
|
|
169
|
+
mkdirSync2(dirname3(filePath), { recursive: true });
|
|
170
|
+
if (existsSync3(filePath) && opts.onConflict) {
|
|
171
|
+
const existing = readFileSync3(filePath, "utf8");
|
|
172
|
+
if (existing !== planned) {
|
|
173
|
+
const decision = await opts.onConflict(filePath, existing, planned);
|
|
174
|
+
if (decision === "skip") return { written: [] };
|
|
175
|
+
if (decision === "rename") {
|
|
176
|
+
const newPath = renamedPath(filePath);
|
|
177
|
+
writeFileSync2(newPath, planned, "utf8");
|
|
178
|
+
return { written: [newPath] };
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
writeFileSync2(filePath, planned, "utf8");
|
|
183
|
+
return { written: [filePath] };
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// src/cli/install/agents.ts
|
|
187
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync4 } from "fs";
|
|
188
|
+
import { join as join5 } from "path";
|
|
189
|
+
|
|
190
|
+
// src/cli/install/markers.ts
|
|
191
|
+
var START = "<!-- jenz:start -->";
|
|
192
|
+
var END = "<!-- jenz:end -->";
|
|
193
|
+
function upsertSection(existing, body) {
|
|
194
|
+
const section = `${START}
|
|
195
|
+
${body}
|
|
196
|
+
${END}`;
|
|
197
|
+
const startIdx = existing.indexOf(START);
|
|
198
|
+
const endIdx = existing.indexOf(END);
|
|
199
|
+
if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) {
|
|
200
|
+
const before = existing.slice(0, startIdx);
|
|
201
|
+
const after = existing.slice(endIdx + END.length);
|
|
202
|
+
return ensureTrailingNewline(`${before}${section}${after}`);
|
|
203
|
+
}
|
|
204
|
+
if (existing.length === 0) return ensureTrailingNewline(section);
|
|
205
|
+
const sep = existing.endsWith("\n\n") ? "" : existing.endsWith("\n") ? "\n" : "\n\n";
|
|
206
|
+
return ensureTrailingNewline(`${existing}${sep}${section}`);
|
|
207
|
+
}
|
|
208
|
+
function ensureTrailingNewline(s) {
|
|
209
|
+
return s.endsWith("\n") ? s : `${s}
|
|
210
|
+
`;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// src/cli/install/agents.ts
|
|
214
|
+
async function installAgentsMd(skillBody, opts = {}) {
|
|
215
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
216
|
+
const filePath = join5(cwd, "AGENTS.md");
|
|
217
|
+
const existing = existsSync4(filePath) ? readFileSync4(filePath, "utf8") : "";
|
|
218
|
+
const sectionBody = `## Jenz Setup
|
|
219
|
+
|
|
220
|
+
${skillBody}`;
|
|
221
|
+
const next = upsertSection(existing, sectionBody);
|
|
222
|
+
writeFileSync3(filePath, next, "utf8");
|
|
223
|
+
return { written: [filePath] };
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// src/cli/install/codex.ts
|
|
227
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, existsSync as existsSync5, mkdirSync as mkdirSync3 } from "fs";
|
|
228
|
+
import { join as join6, dirname as dirname4 } from "path";
|
|
229
|
+
import { homedir as homedir3 } from "os";
|
|
230
|
+
async function installCodex(skillBody, opts = {}) {
|
|
231
|
+
const home = opts.homedir ?? homedir3();
|
|
232
|
+
const filePath = join6(home, ".codex", "instructions.md");
|
|
233
|
+
mkdirSync3(dirname4(filePath), { recursive: true });
|
|
234
|
+
const existing = existsSync5(filePath) ? readFileSync5(filePath, "utf8") : "";
|
|
235
|
+
const sectionBody = `## Jenz Setup
|
|
236
|
+
|
|
237
|
+
${skillBody}`;
|
|
238
|
+
const next = upsertSection(existing, sectionBody);
|
|
239
|
+
writeFileSync4(filePath, next, "utf8");
|
|
240
|
+
return { written: [filePath] };
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// src/cli/init.ts
|
|
244
|
+
var clackConflict = async (filePath, existing, planned) => {
|
|
245
|
+
note(
|
|
246
|
+
`File exists with different content:
|
|
247
|
+
${pc.dim(filePath)}
|
|
248
|
+
|
|
249
|
+
${diffHint(existing, planned)}`,
|
|
250
|
+
"Conflict"
|
|
251
|
+
);
|
|
252
|
+
const choice = await select({
|
|
253
|
+
message: "What should I do?",
|
|
254
|
+
options: [
|
|
255
|
+
{ value: "overwrite", label: "Overwrite", hint: "replace the existing file" },
|
|
256
|
+
{ value: "skip", label: "Skip", hint: "leave the existing file alone" },
|
|
257
|
+
{ value: "rename", label: "Rename", hint: "write to <file>.new" }
|
|
258
|
+
]
|
|
259
|
+
});
|
|
260
|
+
if (isCancel(choice)) {
|
|
261
|
+
cancel("Cancelled.");
|
|
262
|
+
process.exit(0);
|
|
263
|
+
}
|
|
264
|
+
return choice;
|
|
265
|
+
};
|
|
266
|
+
async function runInit() {
|
|
267
|
+
intro(pc.cyan(LOGO) + "\n " + pc.dim(TAGLINE));
|
|
268
|
+
const detected = detectTools();
|
|
269
|
+
const targets = [
|
|
270
|
+
{ id: "claude", label: "Claude Code", hint: "~/.claude/skills/jenz-setup/ + /jenz command", detected: detected.claude },
|
|
271
|
+
{ id: "cursor", label: "Cursor", hint: ".cursor/rules/jenz-setup.mdc", detected: detected.cursor },
|
|
272
|
+
{ id: "agents", label: "AGENTS.md", hint: "./AGENTS.md (section)", detected: detected.agents },
|
|
273
|
+
{ id: "codex", label: "Codex", hint: "~/.codex/instructions.md (section)", detected: detected.codex }
|
|
274
|
+
];
|
|
275
|
+
note(
|
|
276
|
+
targets.map((t) => `${t.detected ? pc.green("\u2713") : pc.dim("\u2717")} ${t.label.padEnd(14)} ${pc.dim(t.hint)}`).join("\n"),
|
|
277
|
+
"Detected tools"
|
|
278
|
+
);
|
|
279
|
+
const selected = await multiselect({
|
|
280
|
+
message: "Where should I install the Jenz setup skill?",
|
|
281
|
+
options: targets.map((t) => ({
|
|
282
|
+
value: t.id,
|
|
283
|
+
label: t.label,
|
|
284
|
+
hint: t.hint
|
|
285
|
+
})),
|
|
286
|
+
initialValues: targets.filter((t) => t.detected).map((t) => t.id),
|
|
287
|
+
required: false
|
|
288
|
+
});
|
|
289
|
+
if (isCancel(selected)) {
|
|
290
|
+
cancel("Cancelled.");
|
|
291
|
+
process.exit(0);
|
|
292
|
+
}
|
|
293
|
+
if (!selected || selected.length === 0) {
|
|
294
|
+
note(
|
|
295
|
+
`No targets selected. To set up Jenz manually, tell your AI tool:
|
|
296
|
+
|
|
297
|
+
${pc.cyan('"Read node_modules/@jenz-ai/sdk/SETUP.md and follow it to add Jenz observability."')}`,
|
|
298
|
+
"Fallback"
|
|
299
|
+
);
|
|
300
|
+
outro("Done.");
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
const skillBody = readSkillBody();
|
|
304
|
+
const written = [];
|
|
305
|
+
for (const id of selected) {
|
|
306
|
+
const t = targets.find((x) => x.id === id);
|
|
307
|
+
try {
|
|
308
|
+
const result = await runInstall(id, skillBody);
|
|
309
|
+
written.push(...result.written);
|
|
310
|
+
const detail = result.written.length === 0 ? pc.dim("(skipped)") : result.written.map((p) => pc.dim(p)).join(", ");
|
|
311
|
+
log.success(`${t.label} \u2192 ${detail}`);
|
|
312
|
+
} catch (err) {
|
|
313
|
+
log.error(`${t.label} \u2014 ${err instanceof Error ? err.message : String(err)}`);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
note(
|
|
317
|
+
`1. Open your AI tool (Claude Code, Cursor, Codex, \u2026)
|
|
318
|
+
2. Say "${pc.cyan("set up jenz")}" or run ${pc.cyan("/jenz")} (Claude only)
|
|
319
|
+
3. The skill walks you through the rest \u2014 it'll wire the SDK,
|
|
320
|
+
ask about your agent, and prompt for an API key.`,
|
|
321
|
+
"Next"
|
|
322
|
+
);
|
|
323
|
+
outro(pc.green("Setup files written. Happy shipping."));
|
|
324
|
+
}
|
|
325
|
+
async function runInstall(id, body) {
|
|
326
|
+
switch (id) {
|
|
327
|
+
case "claude":
|
|
328
|
+
return installClaude(body, { onConflict: clackConflict });
|
|
329
|
+
case "cursor":
|
|
330
|
+
return installCursor(body, { onConflict: clackConflict });
|
|
331
|
+
case "agents":
|
|
332
|
+
return installAgentsMd(body);
|
|
333
|
+
case "codex":
|
|
334
|
+
return installCodex(body);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// src/cli.ts
|
|
339
|
+
var USAGE = `Usage: jenz <command>
|
|
340
|
+
|
|
341
|
+
Commands:
|
|
342
|
+
init Install the Jenz setup skill in your AI coding tools
|
|
343
|
+
|
|
344
|
+
Examples:
|
|
345
|
+
npx @jenz-ai/sdk init
|
|
346
|
+
npx jenz init
|
|
347
|
+
`;
|
|
348
|
+
async function main() {
|
|
349
|
+
const [, , cmd, ...rest] = process.argv;
|
|
350
|
+
if (!cmd || cmd === "--help" || cmd === "-h" || cmd === "help") {
|
|
351
|
+
console.log(USAGE);
|
|
352
|
+
process.exit(0);
|
|
353
|
+
}
|
|
354
|
+
if (rest.length > 0) {
|
|
355
|
+
console.error(`jenz: unexpected arguments: ${rest.join(" ")}`);
|
|
356
|
+
console.error(USAGE);
|
|
357
|
+
process.exit(2);
|
|
358
|
+
}
|
|
359
|
+
switch (cmd) {
|
|
360
|
+
case "init":
|
|
361
|
+
await runInit();
|
|
362
|
+
return;
|
|
363
|
+
default:
|
|
364
|
+
console.error(`jenz: unknown command "${cmd}"`);
|
|
365
|
+
console.error(USAGE);
|
|
366
|
+
process.exit(2);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
main().catch((err) => {
|
|
370
|
+
console.error(err);
|
|
371
|
+
process.exit(1);
|
|
372
|
+
});
|
|
373
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/init.ts","../src/cli/logo.ts","../src/cli/detect.ts","../src/cli/skill-content.ts","../src/cli/install/claude.ts","../src/cli/install/conflict.ts","../src/cli/install/cursor.ts","../src/cli/install/agents.ts","../src/cli/install/markers.ts","../src/cli/install/codex.ts","../src/cli.ts"],"sourcesContent":["import { intro, multiselect, note, outro, isCancel, cancel, select, log } from '@clack/prompts';\nimport pc from 'picocolors';\nimport { LOGO, TAGLINE } from './logo.js';\nimport { detectTools } from './detect.js';\nimport { readSkillBody } from './skill-content.js';\nimport { installClaude } from './install/claude.js';\nimport { installCursor } from './install/cursor.js';\nimport { installAgentsMd } from './install/agents.js';\nimport { installCodex } from './install/codex.js';\nimport type { ConflictHandler } from './install/conflict.js';\nimport { diffHint } from './install/conflict.js';\n\ninterface TargetMeta {\n id: 'claude' | 'cursor' | 'agents' | 'codex';\n label: string;\n hint: string;\n detected: boolean;\n}\n\nconst clackConflict: ConflictHandler = async (filePath, existing, planned) => {\n note(\n `File exists with different content:\\n${pc.dim(filePath)}\\n\\n${diffHint(existing, planned)}`,\n 'Conflict',\n );\n const choice = await select({\n message: 'What should I do?',\n options: [\n { value: 'overwrite' as const, label: 'Overwrite', hint: 'replace the existing file' },\n { value: 'skip' as const, label: 'Skip', hint: 'leave the existing file alone' },\n { value: 'rename' as const, label: 'Rename', hint: 'write to <file>.new' },\n ],\n });\n if (isCancel(choice)) {\n cancel('Cancelled.');\n process.exit(0);\n }\n return choice as 'overwrite' | 'skip' | 'rename';\n};\n\nexport async function runInit(): Promise<void> {\n // Welcome\n intro(pc.cyan(LOGO) + '\\n ' + pc.dim(TAGLINE));\n\n // Detect\n const detected = detectTools();\n const targets: TargetMeta[] = [\n { id: 'claude', label: 'Claude Code', hint: '~/.claude/skills/jenz-setup/ + /jenz command', detected: detected.claude },\n { id: 'cursor', label: 'Cursor', hint: '.cursor/rules/jenz-setup.mdc', detected: detected.cursor },\n { id: 'agents', label: 'AGENTS.md', hint: './AGENTS.md (section)', detected: detected.agents },\n { id: 'codex', label: 'Codex', hint: '~/.codex/instructions.md (section)', detected: detected.codex },\n ];\n\n note(\n targets\n .map((t) => `${t.detected ? pc.green('✓') : pc.dim('✗')} ${t.label.padEnd(14)} ${pc.dim(t.hint)}`)\n .join('\\n'),\n 'Detected tools',\n );\n\n // Multi-select install targets\n const selected = await multiselect({\n message: 'Where should I install the Jenz setup skill?',\n options: targets.map((t) => ({\n value: t.id,\n label: t.label,\n hint: t.hint,\n })),\n initialValues: targets.filter((t) => t.detected).map((t) => t.id),\n required: false,\n });\n\n if (isCancel(selected)) {\n cancel('Cancelled.');\n process.exit(0);\n }\n\n if (!selected || selected.length === 0) {\n note(\n `No targets selected. To set up Jenz manually, tell your AI tool:\\n\\n ${pc.cyan('\"Read node_modules/@jenz-ai/sdk/SETUP.md and follow it to add Jenz observability.\"')}`,\n 'Fallback',\n );\n outro('Done.');\n return;\n }\n\n // Install\n const skillBody = readSkillBody();\n const written: string[] = [];\n\n for (const id of selected) {\n const t = targets.find((x) => x.id === id)!;\n try {\n const result = await runInstall(id as TargetMeta['id'], skillBody);\n written.push(...result.written);\n const detail = result.written.length === 0\n ? pc.dim('(skipped)')\n : result.written.map((p) => pc.dim(p)).join(', ');\n log.success(`${t.label} → ${detail}`);\n } catch (err) {\n log.error(`${t.label} — ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n // Next steps\n note(\n `1. Open your AI tool (Claude Code, Cursor, Codex, …)\\n` +\n `2. Say \"${pc.cyan('set up jenz')}\" or run ${pc.cyan('/jenz')} (Claude only)\\n` +\n `3. The skill walks you through the rest — it'll wire the SDK,\\n` +\n ` ask about your agent, and prompt for an API key.`,\n 'Next',\n );\n outro(pc.green('Setup files written. Happy shipping.'));\n}\n\nasync function runInstall(id: TargetMeta['id'], body: string): Promise<{ written: string[] }> {\n switch (id) {\n case 'claude':\n return installClaude(body, { onConflict: clackConflict });\n case 'cursor':\n return installCursor(body, { onConflict: clackConflict });\n case 'agents':\n return installAgentsMd(body);\n case 'codex':\n return installCodex(body);\n }\n}\n","// Block-shaded ASCII rendering of \"jenz.ai\".\n// Kept as a string constant so init.ts can wrap it in color\n// without depending on a runtime ASCII generator.\nexport const LOGO = `\\\n ░█████\n ░██\n ░██ ░███████ ░████████ ░█████████\n ░██ ░██ ░██ ░██ ░██ ░███\n░██ ░██ ░█████████ ░██ ░██ ░███\n░██ ░██ ░██ ░██ ░██ ░███\n ░██████ ░███████ ░██ ░██ ░█████████ ░██ ░██ ░██ ░██\n`;\n\nexport const TAGLINE = 'observability for AI agents';\n","import { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\n\nexport interface DetectionInputs {\n homedir: string;\n cwd: string;\n /** Colon-separated PATH; defaults to process.env.PATH ?? ''. */\n path?: string;\n}\n\nexport interface DetectionResult {\n claude: boolean;\n cursor: boolean;\n codex: boolean;\n agents: boolean;\n}\n\nconst PATH_SEP = process.platform === 'win32' ? ';' : ':';\n\nfunction isBinaryInPath(binary: string, pathEnv: string): boolean {\n if (!pathEnv) return false;\n for (const dir of pathEnv.split(PATH_SEP)) {\n if (!dir) continue;\n if (existsSync(join(dir, binary))) return true;\n }\n return false;\n}\n\nexport function detectTools(inputs?: DetectionInputs): DetectionResult {\n const home = inputs?.homedir ?? homedir();\n const cwd = inputs?.cwd ?? process.cwd();\n const pathEnv = inputs?.path ?? process.env.PATH ?? '';\n\n return {\n claude: existsSync(join(home, '.claude')),\n cursor: existsSync(join(cwd, '.cursor')) || existsSync(join(home, '.cursor')),\n codex: existsSync(join(home, '.codex')) || isBinaryInPath('codex', pathEnv),\n agents: true,\n };\n}\n","import { readFileSync } from 'node:fs';\nimport { fileURLToPath } from 'node:url';\nimport { dirname, join } from 'node:path';\n\n// Read SETUP.md relative to this file. The published layout is:\n// dist/cli.js ← compiled CLI (this file at runtime)\n// SETUP.md ← package root, two levels up from dist/\n// In source layout (vitest), this file is src/cli/skill-content.ts,\n// so SETUP.md is three levels up.\nfunction findSetupMd(): string {\n const here = dirname(fileURLToPath(import.meta.url));\n // Try packaged layout (dist/cli.js → ../SETUP.md), then source\n // layout (src/cli/skill-content.ts → ../../../SETUP.md).\n const candidates = [\n join(here, '..', 'SETUP.md'),\n join(here, '..', '..', 'SETUP.md'),\n join(here, '..', '..', '..', 'SETUP.md'),\n ];\n for (const p of candidates) {\n try {\n return readFileSync(p, 'utf8');\n } catch {\n // try next\n }\n }\n throw new Error(\n `[jenz] could not locate SETUP.md (tried: ${candidates.join(', ')})`,\n );\n}\n\nlet cached: string | undefined;\n\nexport function readSkillBody(): string {\n if (cached === undefined) cached = findSetupMd();\n return cached;\n}\n","import { writeFileSync, mkdirSync, existsSync, readFileSync } from 'node:fs';\nimport { join, dirname } from 'node:path';\nimport { homedir } from 'node:os';\nimport type { ConflictHandler } from './conflict.js';\nimport { renamedPath } from './conflict.js';\n\nexport interface ClaudeInstallOptions {\n homedir?: string;\n onConflict?: ConflictHandler;\n}\n\nexport interface InstallResult {\n written: string[];\n}\n\nconst SKILL_FRONTMATTER = `---\nname: jenz-setup\ndescription: Set up Jenz observability (jenz.dev) for an AI agent — detects the agent's main loop and LLM calls, asks about logging scope, wires up @jenz-ai/sdk, and prompts for an API key. Use when the user says \"set up jenz\", \"add jenz\", \"monitor my agent with jenz\", or just installed @jenz-ai/sdk.\n---\n`;\n\nconst SLASH_COMMAND = `---\ndescription: Set up Jenz observability for this project\n---\n\nActivate the jenz-setup skill and walk me through wiring up Jenz\nin this project. Use AskUserQuestion for the configuration\nquestions and TodoWrite to track the 6 setup steps.\n`;\n\nasync function writeWithConflictPolicy(\n filePath: string,\n planned: string,\n onConflict: ConflictHandler | undefined,\n): Promise<string | null> {\n mkdirSync(dirname(filePath), { recursive: true });\n if (existsSync(filePath) && onConflict) {\n const existing = readFileSync(filePath, 'utf8');\n if (existing !== planned) {\n const decision = await onConflict(filePath, existing, planned);\n if (decision === 'skip') return null;\n if (decision === 'rename') {\n const newPath = renamedPath(filePath);\n writeFileSync(newPath, planned, 'utf8');\n return newPath;\n }\n }\n }\n writeFileSync(filePath, planned, 'utf8');\n return filePath;\n}\n\nexport async function installClaude(\n skillBody: string,\n opts: ClaudeInstallOptions = {},\n): Promise<InstallResult> {\n const home = opts.homedir ?? homedir();\n const skillPath = join(home, '.claude', 'skills', 'jenz-setup', 'SKILL.md');\n const cmdPath = join(home, '.claude', 'commands', 'jenz.md');\n\n const written: string[] = [];\n const skillResult = await writeWithConflictPolicy(\n skillPath,\n `${SKILL_FRONTMATTER}\\n${skillBody}`,\n opts.onConflict,\n );\n if (skillResult) written.push(skillResult);\n\n const cmdResult = await writeWithConflictPolicy(\n cmdPath,\n SLASH_COMMAND,\n opts.onConflict,\n );\n if (cmdResult) written.push(cmdResult);\n\n return { written };\n}\n","/** Decision returned by an onConflict callback. */\nexport type ConflictDecision = 'overwrite' | 'skip' | 'rename';\n\n/**\n * Called when a target wants to write a file that already exists with\n * different content. The callback decides what to do.\n *\n * - `overwrite`: write the new content over the existing file\n * - `skip`: leave the existing file alone, do not write\n * - `rename`: write the new content to a sibling path (`<file>.new`),\n * leave the existing file alone\n */\nexport type ConflictHandler = (\n filePath: string,\n existing: string,\n planned: string,\n) => Promise<ConflictDecision>;\n\n/**\n * Insert `.new` before the file extension. If no extension, append `.new`.\n * Examples:\n * foo.md → foo.new.md\n * bar.tar.gz → bar.tar.new.gz\n * plain → plain.new\n */\nexport function renamedPath(filePath: string): string {\n const dotIdx = filePath.lastIndexOf('.');\n const slashIdx = Math.max(filePath.lastIndexOf('/'), filePath.lastIndexOf('\\\\'));\n if (dotIdx <= slashIdx) return `${filePath}.new`;\n return `${filePath.slice(0, dotIdx)}.new${filePath.slice(dotIdx)}`;\n}\n\n/**\n * Generate a human-readable 3-line diff hint between existing and planned\n * content. Used by the clack-based prompt to show users WHAT differs.\n */\nexport function diffHint(existing: string, planned: string): string {\n const exLines = existing.split('\\n');\n const plLines = planned.split('\\n');\n let firstDiff = -1;\n const max = Math.max(exLines.length, plLines.length);\n for (let i = 0; i < max; i++) {\n if (exLines[i] !== plLines[i]) {\n firstDiff = i;\n break;\n }\n }\n if (firstDiff === -1) return 'Files match (no diff).';\n const exLine = exLines[firstDiff] ?? '(end of file)';\n const plLine = plLines[firstDiff] ?? '(end of file)';\n const truncated = (s: string) => (s.length > 80 ? `${s.slice(0, 77)}...` : s);\n return [\n `existing ${exLines.length} lines, new ${plLines.length} lines (first diff at line ${firstDiff + 1})`,\n ` - ${truncated(exLine)}`,\n ` + ${truncated(plLine)}`,\n ].join('\\n');\n}\n","import { writeFileSync, mkdirSync, existsSync, readFileSync } from 'node:fs';\nimport { join, dirname } from 'node:path';\nimport type { ConflictHandler } from './conflict.js';\nimport { renamedPath } from './conflict.js';\n\nexport interface CursorInstallOptions {\n cwd?: string;\n onConflict?: ConflictHandler;\n}\n\nexport interface InstallResult {\n written: string[];\n}\n\nconst FRONTMATTER = `---\ndescription: Jenz observability setup playbook — wires up @jenz-ai/sdk in this project. Activate when the user asks to set up jenz, add observability, or track agent runs.\nalwaysApply: false\n---\n`;\n\nexport async function installCursor(\n skillBody: string,\n opts: CursorInstallOptions = {},\n): Promise<InstallResult> {\n const cwd = opts.cwd ?? process.cwd();\n const filePath = join(cwd, '.cursor', 'rules', 'jenz-setup.mdc');\n const planned = `${FRONTMATTER}\\n${skillBody}`;\n\n mkdirSync(dirname(filePath), { recursive: true });\n\n if (existsSync(filePath) && opts.onConflict) {\n const existing = readFileSync(filePath, 'utf8');\n if (existing !== planned) {\n const decision = await opts.onConflict(filePath, existing, planned);\n if (decision === 'skip') return { written: [] };\n if (decision === 'rename') {\n const newPath = renamedPath(filePath);\n writeFileSync(newPath, planned, 'utf8');\n return { written: [newPath] };\n }\n // 'overwrite' falls through\n }\n }\n\n writeFileSync(filePath, planned, 'utf8');\n return { written: [filePath] };\n}\n","import { readFileSync, writeFileSync, existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { upsertSection } from './markers.js';\n\nexport interface InstallOptions {\n cwd?: string;\n}\n\nexport interface InstallResult {\n written: string[];\n}\n\nexport async function installAgentsMd(\n skillBody: string,\n opts: InstallOptions = {},\n): Promise<InstallResult> {\n const cwd = opts.cwd ?? process.cwd();\n const filePath = join(cwd, 'AGENTS.md');\n\n const existing = existsSync(filePath) ? readFileSync(filePath, 'utf8') : '';\n const sectionBody = `## Jenz Setup\\n\\n${skillBody}`;\n const next = upsertSection(existing, sectionBody);\n\n writeFileSync(filePath, next, 'utf8');\n return { written: [filePath] };\n}\n","const START = '<!-- jenz:start -->';\nconst END = '<!-- jenz:end -->';\n\n/**\n * Insert or replace a Jenz-owned section in `existing` content.\n *\n * - If markers exist: replace the body between them (idempotent).\n * - If markers don't exist: append a fresh section at the end.\n *\n * The returned content always ends with a single newline.\n */\nexport function upsertSection(existing: string, body: string): string {\n const section = `${START}\\n${body}\\n${END}`;\n const startIdx = existing.indexOf(START);\n const endIdx = existing.indexOf(END);\n\n if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) {\n const before = existing.slice(0, startIdx);\n const after = existing.slice(endIdx + END.length);\n return ensureTrailingNewline(`${before}${section}${after}`);\n }\n\n // Append: separate from existing content with a blank line if\n // existing isn't empty and doesn't already end in a blank line.\n if (existing.length === 0) return ensureTrailingNewline(section);\n const sep = existing.endsWith('\\n\\n') ? '' : existing.endsWith('\\n') ? '\\n' : '\\n\\n';\n return ensureTrailingNewline(`${existing}${sep}${section}`);\n}\n\nfunction ensureTrailingNewline(s: string): string {\n return s.endsWith('\\n') ? s : `${s}\\n`;\n}\n","import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';\nimport { join, dirname } from 'node:path';\nimport { homedir } from 'node:os';\nimport { upsertSection } from './markers.js';\n\nexport interface CodexInstallOptions {\n homedir?: string;\n}\n\nexport interface InstallResult {\n written: string[];\n}\n\nexport async function installCodex(\n skillBody: string,\n opts: CodexInstallOptions = {},\n): Promise<InstallResult> {\n const home = opts.homedir ?? homedir();\n const filePath = join(home, '.codex', 'instructions.md');\n\n mkdirSync(dirname(filePath), { recursive: true });\n const existing = existsSync(filePath) ? readFileSync(filePath, 'utf8') : '';\n const sectionBody = `## Jenz Setup\\n\\n${skillBody}`;\n const next = upsertSection(existing, sectionBody);\n\n writeFileSync(filePath, next, 'utf8');\n return { written: [filePath] };\n}\n","import { runInit } from './cli/init.js';\n\nconst USAGE = `\\\nUsage: jenz <command>\n\nCommands:\n init Install the Jenz setup skill in your AI coding tools\n\nExamples:\n npx @jenz-ai/sdk init\n npx jenz init\n`;\n\nasync function main(): Promise<void> {\n const [, , cmd, ...rest] = process.argv;\n\n if (!cmd || cmd === '--help' || cmd === '-h' || cmd === 'help') {\n console.log(USAGE);\n process.exit(0);\n }\n\n if (rest.length > 0) {\n console.error(`jenz: unexpected arguments: ${rest.join(' ')}`);\n console.error(USAGE);\n process.exit(2);\n }\n\n switch (cmd) {\n case 'init':\n await runInit();\n return;\n default:\n console.error(`jenz: unknown command \"${cmd}\"`);\n console.error(USAGE);\n process.exit(2);\n }\n}\n\nmain().catch((err) => {\n console.error(err);\n process.exit(1);\n});\n"],"mappings":"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","names":["join","existsSync","readFileSync","join","dirname","homedir","dirname","existsSync","readFileSync","homedir","join","writeFileSync","mkdirSync","existsSync","readFileSync","join","dirname","join","mkdirSync","dirname","existsSync","readFileSync","writeFileSync","readFileSync","writeFileSync","existsSync","join","join","existsSync","readFileSync","writeFileSync","readFileSync","writeFileSync","existsSync","mkdirSync","join","dirname","homedir","homedir","join","mkdirSync","dirname","existsSync","readFileSync","writeFileSync"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jenz-ai/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Client SDK for Jenz — observability for AI agents (runs, events, costs, latency).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -18,7 +18,10 @@
|
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
},
|
|
21
|
-
"
|
|
21
|
+
"bin": {
|
|
22
|
+
"jenz": "./dist/cli.js"
|
|
23
|
+
},
|
|
24
|
+
"files": ["dist", "README.md", "SETUP.md"],
|
|
22
25
|
"license": "MIT",
|
|
23
26
|
"homepage": "https://github.com/jenz-ai/jenz-mvp/tree/main/packages/sdk",
|
|
24
27
|
"repository": {
|
|
@@ -29,15 +32,7 @@
|
|
|
29
32
|
"bugs": {
|
|
30
33
|
"url": "https://github.com/jenz-ai/jenz-mvp/issues"
|
|
31
34
|
},
|
|
32
|
-
"keywords": [
|
|
33
|
-
"ai",
|
|
34
|
-
"agent",
|
|
35
|
-
"observability",
|
|
36
|
-
"tracing",
|
|
37
|
-
"llm",
|
|
38
|
-
"telemetry",
|
|
39
|
-
"jenz"
|
|
40
|
-
],
|
|
35
|
+
"keywords": ["ai", "agent", "observability", "tracing", "llm", "telemetry", "jenz"],
|
|
41
36
|
"publishConfig": {
|
|
42
37
|
"access": "public"
|
|
43
38
|
},
|
|
@@ -48,6 +43,10 @@
|
|
|
48
43
|
"typecheck": "tsc --noEmit",
|
|
49
44
|
"prepublishOnly": "pnpm build && pnpm test"
|
|
50
45
|
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"@clack/prompts": "^0.7.0",
|
|
48
|
+
"picocolors": "^1.1.1"
|
|
49
|
+
},
|
|
51
50
|
"devDependencies": {
|
|
52
51
|
"@types/node": "^22.9.0",
|
|
53
52
|
"tsup": "^8.3.5",
|