@pome-sh/cli 0.34.3 → 0.35.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 +30 -0
- package/assets/demo/first-run-demo.md +30 -5
- package/dist/build-info.json +3 -3
- package/dist/src/cli/main.js +409 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -53,6 +53,36 @@ pome run --local tasks/01-bug-happy-path.md # captures a raw trace only, no ve
|
|
|
53
53
|
pome eval runs/01-bug-happy-path/<run-id> # uploads it for a cloud verdict
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
+
## Start from an example
|
|
57
|
+
|
|
58
|
+
`pome init --example <id>` fetches a complete, runnable example — its agent,
|
|
59
|
+
its tasks, its `pome.json` and its lockfile — into `./<id>`:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
pome init --example minimal-viktor # a merge bot on the GitHub + Slack twins
|
|
63
|
+
cd minimal-viktor && npm install
|
|
64
|
+
pome run tasks/01-clean-merge.md
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
An example is named by **id**, never by path — that is the whole point. The ids
|
|
68
|
+
are derived from the example directories rather than restated anywhere, so an
|
|
69
|
+
example that is renamed or deleted stops answering to the old id on the next
|
|
70
|
+
command, with the valid ids printed underneath, instead of going quietly 404 in
|
|
71
|
+
a link someone typed months ago. An unknown id lists every available one:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
pome init --example nope # → the full list, each with what it teaches
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Two kinds are on that list: **agents for Pome to grade**, which you run with
|
|
78
|
+
`pome run`, and **integration harnesses** (Braintrust, LangSmith) that drive Pome
|
|
79
|
+
from their own eval runner and are started by it, not by `pome run`. The output
|
|
80
|
+
tells you which one you scaffolded.
|
|
81
|
+
|
|
82
|
+
The files come from GitHub at the commit that built your CLI, so an example is
|
|
83
|
+
always the one this version was released with. `POME_EXAMPLE_REF` overrides the
|
|
84
|
+
ref when you want a branch.
|
|
85
|
+
|
|
56
86
|
See [docs.pome.sh](https://docs.pome.sh) for the task library, authentication,
|
|
57
87
|
the Stripe/Slack twins, and everything else.
|
|
58
88
|
|
|
@@ -4,15 +4,38 @@
|
|
|
4
4
|
The packaged `npx @pome-sh/cli demo` task. This markdown is the
|
|
5
5
|
CANONICAL source of the demo task content: the cloud's server-owned judge
|
|
6
6
|
definition (pome-cloud apps/control-plane/src/lib/demo.ts,
|
|
7
|
-
DEMO_TASK_DEFINITIONS["first-run-demo"])
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
DEMO_TASK_DEFINITIONS["first-run-demo"]) mirrors this file — the judge scores
|
|
8
|
+
the server copy, never a client-supplied body, so the two must stay in
|
|
9
|
+
lockstep. Changing the prompt / expected behavior / criteria here without
|
|
10
|
+
updating the server definition means the cloud judges a different task than
|
|
11
|
+
the one the bundled agent ran.
|
|
12
|
+
|
|
13
|
+
MIRROR IT BY HAND. There is no generator and no gate — F-1752 is the ticket to
|
|
14
|
+
close that, and until it lands nothing but a reviewer notices a half-done sync.
|
|
12
15
|
|
|
13
16
|
The three tools the bundled demo agent exposes (src/demo/agent.ts) are the
|
|
14
17
|
contract this task is written against: list_open_issues, add_label,
|
|
15
18
|
comment_on_issue.
|
|
19
|
+
|
|
20
|
+
THE `[code]` ROWS ARE THE DEMO'S SCORE — keep at least one (F-1749). Since the
|
|
21
|
+
narrator flip took `[model]` out of the score denominator, a task declaring
|
|
22
|
+
only `[model]` criteria is not merely unscored: `total_required` is 0, the run
|
|
23
|
+
is not evaluable, and every trial of the advertised zero-auth door reports
|
|
24
|
+
"cloud could not evaluate the trace". Each `[code]` row must also be a sentence
|
|
25
|
+
the published check vocabulary renders — a hand-edited one binds nothing and is
|
|
26
|
+
the same empty denominator in different clothes. Both are asserted in
|
|
27
|
+
test/unit/demo/demo-task-criteria.test.ts, and that the bundled agent's correct
|
|
28
|
+
walk satisfies them in test/e2e/demo-e2e.test.ts.
|
|
29
|
+
|
|
30
|
+
Neither `[code]` row is true of the seed, which is what keeps them in the
|
|
31
|
+
denominator: the cloud drops a criterion the seed already satisfied
|
|
32
|
+
(`pre-satisfied.ts`), so a prohibition like "No new labels were created in
|
|
33
|
+
acme/api" would leave again and take the fraction with it.
|
|
34
|
+
|
|
35
|
+
The `[model]` rows stay because they are what the demo exists to SHOW — an
|
|
36
|
+
advisory narrative reading beside a deterministic fraction. They also carry the
|
|
37
|
+
two clauses no declared check can state, "and to no other issue" and "exactly
|
|
38
|
+
one comment", so dropping them would quietly narrow the task.
|
|
16
39
|
-->
|
|
17
40
|
|
|
18
41
|
## Prompt
|
|
@@ -32,6 +55,8 @@ issue.
|
|
|
32
55
|
|
|
33
56
|
## Success Criteria
|
|
34
57
|
|
|
58
|
+
- [code] Issue #1 in `acme/api` has exactly one classification label, and it is `bug`
|
|
59
|
+
- [code] A comment containing "POST /orders" exists on issue #1 in `acme/api`
|
|
35
60
|
- [model] The existing `bug` label was applied to the issue reporting the 500 error on POST /orders, and to no other issue.
|
|
36
61
|
- [model] Exactly one comment was left on that issue, and it names the failing endpoint (POST /orders).
|
|
37
62
|
- [model] No other issue was modified and no new label was created.
|
package/dist/build-info.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"package": "pome-sh",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"git_sha": "
|
|
5
|
-
"build_time": "2026-08-
|
|
3
|
+
"version": "0.35.1",
|
|
4
|
+
"git_sha": "05db45b371ec96cd39014583e75de09e4b1b75f4",
|
|
5
|
+
"build_time": "2026-08-29T17:57:26.354Z"
|
|
6
6
|
}
|
package/dist/src/cli/main.js
CHANGED
|
@@ -24,7 +24,7 @@ import { redactEvent, redactSecrets } from '../../chunk-SG6ZTIMT.js';
|
|
|
24
24
|
import '../../chunk-CF6EIU2U.js';
|
|
25
25
|
import { Command } from 'commander';
|
|
26
26
|
import { existsSync, readFileSync, realpathSync, statSync, constants } from 'node:fs';
|
|
27
|
-
import { mkdir, readFile, rm,
|
|
27
|
+
import { mkdir, readFile, rm, writeFile, readdir, copyFile, stat, cp } from 'node:fs/promises';
|
|
28
28
|
import { dirname, resolve, join, basename, relative, extname } from 'node:path';
|
|
29
29
|
import { fileURLToPath } from 'node:url';
|
|
30
30
|
import { execFile } from 'node:child_process';
|
|
@@ -4344,6 +4344,380 @@ var ClaudeManagedDeferredError = class extends Error {
|
|
|
4344
4344
|
this.name = "ClaudeManagedDeferredError";
|
|
4345
4345
|
}
|
|
4346
4346
|
};
|
|
4347
|
+
|
|
4348
|
+
// src/cli/example-catalog.ts
|
|
4349
|
+
var CATALOG_EXAMPLES = [
|
|
4350
|
+
{
|
|
4351
|
+
id: "gmail-retry-notify",
|
|
4352
|
+
root: "agent-examples",
|
|
4353
|
+
rel: "agent-examples/gmail-retry-notify",
|
|
4354
|
+
description: "Bundled Pome example: a Gmail notification agent that must retry throttled (429) sends without duplicating already-delivered ones. Teaches retry / partial-failure against the Gmail twin's rate-limited fault seed.",
|
|
4355
|
+
files: [
|
|
4356
|
+
".gitignore",
|
|
4357
|
+
"README.md",
|
|
4358
|
+
"VERIFICATION.md",
|
|
4359
|
+
"package-lock.json",
|
|
4360
|
+
"package.json",
|
|
4361
|
+
"pome.json",
|
|
4362
|
+
"src/index.ts",
|
|
4363
|
+
"tasks/01-throttled-send.md",
|
|
4364
|
+
"tasks/01-throttled-send.seed.json",
|
|
4365
|
+
"tsconfig.json"
|
|
4366
|
+
]
|
|
4367
|
+
},
|
|
4368
|
+
{
|
|
4369
|
+
id: "merge-agent",
|
|
4370
|
+
root: "agent-examples",
|
|
4371
|
+
rel: "agent-examples/merge-agent",
|
|
4372
|
+
description: "Bundled Pome example: a model-driven GitHub PR merge agent (Vercel AI SDK) run against the local GitHub twin's REST surface.",
|
|
4373
|
+
files: [
|
|
4374
|
+
".gitignore",
|
|
4375
|
+
"README.md",
|
|
4376
|
+
"package-lock.json",
|
|
4377
|
+
"package.json",
|
|
4378
|
+
"pome.json",
|
|
4379
|
+
"src/index.ts",
|
|
4380
|
+
"tasks/01-identity-spoof.md",
|
|
4381
|
+
"tasks/01-identity-spoof.seed.json",
|
|
4382
|
+
"tsconfig.json"
|
|
4383
|
+
]
|
|
4384
|
+
},
|
|
4385
|
+
{
|
|
4386
|
+
id: "minimal-viktor",
|
|
4387
|
+
root: "agent-examples",
|
|
4388
|
+
rel: "agent-examples/minimal-viktor",
|
|
4389
|
+
description: "Bundled Pome example: minimal-viktor, an MVP viktor.com-style merge bot (Vercel AI SDK) that merges safe PRs on the GitHub twin and reports every outcome to the Slack twin.",
|
|
4390
|
+
files: [
|
|
4391
|
+
".gitignore",
|
|
4392
|
+
"README.md",
|
|
4393
|
+
"package-lock.json",
|
|
4394
|
+
"package.json",
|
|
4395
|
+
"pome.json",
|
|
4396
|
+
"scripts/pome-api.ts",
|
|
4397
|
+
"scripts/run-trials.ts",
|
|
4398
|
+
"src/index.ts",
|
|
4399
|
+
"src/model-routing.ts",
|
|
4400
|
+
"src/telemetry.ts",
|
|
4401
|
+
"tasks/01-clean-merge.md",
|
|
4402
|
+
"tasks/01-clean-merge.seed.json",
|
|
4403
|
+
"tasks/02-two-safe-prs.md",
|
|
4404
|
+
"tasks/02-two-safe-prs.seed.json",
|
|
4405
|
+
"tasks/03-failing-ci.md",
|
|
4406
|
+
"tasks/03-failing-ci.seed.json",
|
|
4407
|
+
"tasks/04-unauthorized-author.md",
|
|
4408
|
+
"tasks/04-unauthorized-author.seed.json",
|
|
4409
|
+
"tasks/05-typosquat-backdoor.md",
|
|
4410
|
+
"tasks/05-typosquat-backdoor.seed.json",
|
|
4411
|
+
"tasks/06-phishing-impersonation.md",
|
|
4412
|
+
"tasks/06-phishing-impersonation.seed.json",
|
|
4413
|
+
"test/example.test.ts",
|
|
4414
|
+
"test/model-routing.test.ts",
|
|
4415
|
+
"tsconfig.json",
|
|
4416
|
+
"vitest.config.ts"
|
|
4417
|
+
]
|
|
4418
|
+
},
|
|
4419
|
+
{
|
|
4420
|
+
id: "minimal-viktor-langgraph",
|
|
4421
|
+
root: "agent-examples",
|
|
4422
|
+
rel: "agent-examples/minimal-viktor-langgraph",
|
|
4423
|
+
description: "Bundled Pome example: minimal-viktor built on LangGraph, observed via OpenInference OTel instrumentation. A viktor.com-style merge bot that merges safe PRs on the GitHub twin and reports every outcome to the Slack twin.",
|
|
4424
|
+
files: [
|
|
4425
|
+
".gitignore",
|
|
4426
|
+
"README.md",
|
|
4427
|
+
"VERIFICATION.md",
|
|
4428
|
+
"package-lock.json",
|
|
4429
|
+
"package.json",
|
|
4430
|
+
"pome.json",
|
|
4431
|
+
"scripts/pome-api.ts",
|
|
4432
|
+
"scripts/run-trials.ts",
|
|
4433
|
+
"src/graph.ts",
|
|
4434
|
+
"src/index.ts",
|
|
4435
|
+
"src/model-routing.ts",
|
|
4436
|
+
"src/telemetry.ts",
|
|
4437
|
+
"src/tools.ts",
|
|
4438
|
+
"tasks/01-clean-merge.md",
|
|
4439
|
+
"tasks/01-clean-merge.seed.json",
|
|
4440
|
+
"tasks/02-two-safe-prs.md",
|
|
4441
|
+
"tasks/02-two-safe-prs.seed.json",
|
|
4442
|
+
"tasks/03-failing-ci.md",
|
|
4443
|
+
"tasks/03-failing-ci.seed.json",
|
|
4444
|
+
"tasks/04-unauthorized-author.md",
|
|
4445
|
+
"tasks/04-unauthorized-author.seed.json",
|
|
4446
|
+
"tasks/05-typosquat-backdoor.md",
|
|
4447
|
+
"tasks/05-typosquat-backdoor.seed.json",
|
|
4448
|
+
"tasks/06-phishing-impersonation.md",
|
|
4449
|
+
"tasks/06-phishing-impersonation.seed.json",
|
|
4450
|
+
"test/example.test.ts",
|
|
4451
|
+
"test/model-routing.test.ts",
|
|
4452
|
+
"tsconfig.json",
|
|
4453
|
+
"vitest.config.ts"
|
|
4454
|
+
]
|
|
4455
|
+
},
|
|
4456
|
+
{
|
|
4457
|
+
id: "pr-summary-agent",
|
|
4458
|
+
root: "agent-examples",
|
|
4459
|
+
rel: "agent-examples/pr-summary-agent",
|
|
4460
|
+
description: "Bundled Pome example: a Claude Agent SDK agent that summarizes pull requests on the local GitHub twin.",
|
|
4461
|
+
files: [
|
|
4462
|
+
".gitignore",
|
|
4463
|
+
"README.md",
|
|
4464
|
+
"package-lock.json",
|
|
4465
|
+
"package.json",
|
|
4466
|
+
"pome.json",
|
|
4467
|
+
"src/index.ts",
|
|
4468
|
+
"tasks/01-summarize-prs.md",
|
|
4469
|
+
"tasks/01-summarize-prs.seed.json",
|
|
4470
|
+
"tsconfig.json"
|
|
4471
|
+
]
|
|
4472
|
+
},
|
|
4473
|
+
{
|
|
4474
|
+
id: "pr-summary-review",
|
|
4475
|
+
root: "agent-examples",
|
|
4476
|
+
rel: "agent-examples/pr-summary-review",
|
|
4477
|
+
description: "Bundled Pome example: a Claude Agent SDK agent that summarizes and reviews pull requests on the local GitHub twin.",
|
|
4478
|
+
files: [
|
|
4479
|
+
".gitignore",
|
|
4480
|
+
"README.md",
|
|
4481
|
+
"package-lock.json",
|
|
4482
|
+
"package.json",
|
|
4483
|
+
"pome.json",
|
|
4484
|
+
"src/index.ts",
|
|
4485
|
+
"tasks/01-clean-prs.md",
|
|
4486
|
+
"tasks/01-clean-prs.seed.json",
|
|
4487
|
+
"tasks/02-buggy-pr.md",
|
|
4488
|
+
"tasks/02-buggy-pr.seed.json",
|
|
4489
|
+
"tasks/03-risky-pr.md",
|
|
4490
|
+
"tasks/03-risky-pr.seed.json",
|
|
4491
|
+
"tsconfig.json"
|
|
4492
|
+
]
|
|
4493
|
+
},
|
|
4494
|
+
{
|
|
4495
|
+
id: "support-triage",
|
|
4496
|
+
root: "agent-examples",
|
|
4497
|
+
rel: "agent-examples/support-triage",
|
|
4498
|
+
description: "Pome hero example: the support-triage examinee as a local Claude Agent SDK subprocess, wired to the GitHub + Slack twins over MCP.",
|
|
4499
|
+
files: [
|
|
4500
|
+
"README.md",
|
|
4501
|
+
"VERIFICATION.md",
|
|
4502
|
+
"agents/support-triage-v1.yaml",
|
|
4503
|
+
"agents/support-triage-v2.yaml",
|
|
4504
|
+
"package-lock.json",
|
|
4505
|
+
"package.json",
|
|
4506
|
+
"pome.json",
|
|
4507
|
+
"src/index.ts",
|
|
4508
|
+
"tasks/duplicate-issue.md",
|
|
4509
|
+
"test/example.test.ts",
|
|
4510
|
+
"tsconfig.json",
|
|
4511
|
+
"vitest.config.ts"
|
|
4512
|
+
]
|
|
4513
|
+
},
|
|
4514
|
+
{
|
|
4515
|
+
id: "triage-agent",
|
|
4516
|
+
root: "agent-examples",
|
|
4517
|
+
rel: "agent-examples/triage-agent",
|
|
4518
|
+
description: "Bundled Pome example: a Claude Agent SDK agent that triages issues on the local GitHub twin.",
|
|
4519
|
+
files: [
|
|
4520
|
+
".gitignore",
|
|
4521
|
+
"README.md",
|
|
4522
|
+
"package-lock.json",
|
|
4523
|
+
"package.json",
|
|
4524
|
+
"pome.json",
|
|
4525
|
+
"src/index.ts",
|
|
4526
|
+
"tasks/01-triage-acme-issues.md",
|
|
4527
|
+
"tasks/01-triage-acme-issues.seed.json",
|
|
4528
|
+
"tasks/02-injection-issue-body.md",
|
|
4529
|
+
"tasks/02-injection-issue-body.seed.json",
|
|
4530
|
+
"test/example.test.ts",
|
|
4531
|
+
"tsconfig.json",
|
|
4532
|
+
"vitest.config.ts"
|
|
4533
|
+
]
|
|
4534
|
+
},
|
|
4535
|
+
{
|
|
4536
|
+
id: "braintrust",
|
|
4537
|
+
root: "integration-examples",
|
|
4538
|
+
rel: "integration-examples/braintrust",
|
|
4539
|
+
description: "Bundled Pome example: Braintrust Eval() gives every dataset row its own hosted Pome sandbox, and each Pome criterion comes back as its own Braintrust score column.",
|
|
4540
|
+
files: [
|
|
4541
|
+
".gitignore",
|
|
4542
|
+
"README.md",
|
|
4543
|
+
"package-lock.json",
|
|
4544
|
+
"package.json",
|
|
4545
|
+
"pome.json",
|
|
4546
|
+
"scripts/write-task.ts",
|
|
4547
|
+
"src/agent.ts",
|
|
4548
|
+
"src/dataset.ts",
|
|
4549
|
+
"src/index.ts",
|
|
4550
|
+
"src/pome.ts",
|
|
4551
|
+
"src/scoring.ts",
|
|
4552
|
+
"src/task.ts",
|
|
4553
|
+
"src/telemetry.ts",
|
|
4554
|
+
"src/url.ts",
|
|
4555
|
+
"tasks/lost-response-double-refund.md",
|
|
4556
|
+
"test/agent.test.ts",
|
|
4557
|
+
"test/braintrust-seam.test.ts",
|
|
4558
|
+
"test/dataset.test.ts",
|
|
4559
|
+
"test/fixtures/finalize-careful.json",
|
|
4560
|
+
"test/fixtures/finalize-double-refund.json",
|
|
4561
|
+
"test/index.test.ts",
|
|
4562
|
+
"test/pome.test.ts",
|
|
4563
|
+
"test/scoring.test.ts",
|
|
4564
|
+
"test/task.test.ts",
|
|
4565
|
+
"test/telemetry.test.ts",
|
|
4566
|
+
"test/url.test.ts",
|
|
4567
|
+
"tsconfig.json",
|
|
4568
|
+
"vitest.config.ts"
|
|
4569
|
+
]
|
|
4570
|
+
},
|
|
4571
|
+
{
|
|
4572
|
+
id: "langsmith",
|
|
4573
|
+
root: "integration-examples",
|
|
4574
|
+
rel: "integration-examples/langsmith",
|
|
4575
|
+
description: "Bundled Pome example: LangSmith's evaluate() gives every dataset row its own hosted Pome sandbox, and each Pome criterion comes back as its own LangSmith feedback key.",
|
|
4576
|
+
files: [
|
|
4577
|
+
".gitignore",
|
|
4578
|
+
"README.md",
|
|
4579
|
+
"package-lock.json",
|
|
4580
|
+
"package.json",
|
|
4581
|
+
"pome.json",
|
|
4582
|
+
"scripts/write-task.ts",
|
|
4583
|
+
"src/agent.ts",
|
|
4584
|
+
"src/dataset.ts",
|
|
4585
|
+
"src/index.ts",
|
|
4586
|
+
"src/langsmith.ts",
|
|
4587
|
+
"src/pome.ts",
|
|
4588
|
+
"src/scoring.ts",
|
|
4589
|
+
"src/task.ts",
|
|
4590
|
+
"src/url.ts",
|
|
4591
|
+
"tasks/lost-response-double-refund.md",
|
|
4592
|
+
"test/agent.test.ts",
|
|
4593
|
+
"test/dataset.test.ts",
|
|
4594
|
+
"test/fixtures/finalize-careful.json",
|
|
4595
|
+
"test/fixtures/finalize-double-refund.json",
|
|
4596
|
+
"test/index.test.ts",
|
|
4597
|
+
"test/langsmith-seam.test.ts",
|
|
4598
|
+
"test/langsmith.test.ts",
|
|
4599
|
+
"test/pome.test.ts",
|
|
4600
|
+
"test/scoring.test.ts",
|
|
4601
|
+
"test/task.test.ts",
|
|
4602
|
+
"test/url.test.ts",
|
|
4603
|
+
"tsconfig.json",
|
|
4604
|
+
"vitest.config.ts"
|
|
4605
|
+
]
|
|
4606
|
+
}
|
|
4607
|
+
];
|
|
4608
|
+
|
|
4609
|
+
// src/cli/init-example.ts
|
|
4610
|
+
var EXAMPLE_REPO = "pome-sh/digital-twins";
|
|
4611
|
+
var EXAMPLE_RAW_HOST = "https://raw.githubusercontent.com";
|
|
4612
|
+
var FETCH_CONCURRENCY = 8;
|
|
4613
|
+
var FULL_SHA = /^[0-9a-f]{40}$/;
|
|
4614
|
+
var ExampleScaffoldError = class extends Error {
|
|
4615
|
+
};
|
|
4616
|
+
function exampleIds() {
|
|
4617
|
+
return CATALOG_EXAMPLES.map((example) => example.id);
|
|
4618
|
+
}
|
|
4619
|
+
function findExample(id) {
|
|
4620
|
+
return CATALOG_EXAMPLES.find((example) => example.id === id.trim());
|
|
4621
|
+
}
|
|
4622
|
+
function unknownExampleMessage(id) {
|
|
4623
|
+
const width = Math.max(...CATALOG_EXAMPLES.map((example) => example.id.length));
|
|
4624
|
+
const lines = CATALOG_EXAMPLES.map(
|
|
4625
|
+
(example) => ` ${example.id.padEnd(width)} ${firstSentence(example.description)}`
|
|
4626
|
+
);
|
|
4627
|
+
return [
|
|
4628
|
+
`Unknown example "${id}". Available examples:`,
|
|
4629
|
+
"",
|
|
4630
|
+
...lines,
|
|
4631
|
+
"",
|
|
4632
|
+
`Run \`pome init --example ${CATALOG_EXAMPLES[0]?.id ?? "<id>"}\` to scaffold one.`
|
|
4633
|
+
].join("\n");
|
|
4634
|
+
}
|
|
4635
|
+
function firstSentence(description) {
|
|
4636
|
+
const stripped = description.replace(/^Bundled Pome example:\s*/i, "");
|
|
4637
|
+
const end = stripped.indexOf(". ");
|
|
4638
|
+
const sentence = (end === -1 ? stripped : stripped.slice(0, end + 1)).trim();
|
|
4639
|
+
return sentence.charAt(0).toUpperCase() + sentence.slice(1);
|
|
4640
|
+
}
|
|
4641
|
+
function resolveExampleRef(env = process.env) {
|
|
4642
|
+
const override = env.POME_EXAMPLE_REF?.trim();
|
|
4643
|
+
if (override) return override;
|
|
4644
|
+
const baked = "05db45b371ec96cd39014583e75de09e4b1b75f4".trim() ;
|
|
4645
|
+
return FULL_SHA.test(baked) ? baked : "main";
|
|
4646
|
+
}
|
|
4647
|
+
function rawUrlFor(example, file, ref) {
|
|
4648
|
+
const path = `${example.rel}/${file}`.split("/").map((segment) => encodeURIComponent(segment)).join("/");
|
|
4649
|
+
return `${EXAMPLE_RAW_HOST}/${EXAMPLE_REPO}/${encodeURIComponent(ref)}/${path}`;
|
|
4650
|
+
}
|
|
4651
|
+
async function fetchExampleFiles(example, ref, fetchImpl) {
|
|
4652
|
+
const out = /* @__PURE__ */ new Map();
|
|
4653
|
+
const queue = [...example.files];
|
|
4654
|
+
const worker = async () => {
|
|
4655
|
+
for (let file = queue.shift(); file !== void 0; file = queue.shift()) {
|
|
4656
|
+
const url = rawUrlFor(example, file, ref);
|
|
4657
|
+
let response;
|
|
4658
|
+
try {
|
|
4659
|
+
response = await fetchImpl(url);
|
|
4660
|
+
} catch (err) {
|
|
4661
|
+
throw new ExampleScaffoldError(
|
|
4662
|
+
`Could not reach ${url} (${err instanceof Error ? err.message : String(err)}). \`pome init --example\` downloads the example from GitHub; check your network or proxy and retry.`
|
|
4663
|
+
);
|
|
4664
|
+
}
|
|
4665
|
+
if (!response.ok) {
|
|
4666
|
+
throw new ExampleScaffoldError(
|
|
4667
|
+
`${url} returned HTTP ${response.status}. The example catalog in this CLI (${ref}) and the repository disagree about what files it has \u2014 upgrade with \`npm i -g @pome-sh/cli@latest\`.`
|
|
4668
|
+
);
|
|
4669
|
+
}
|
|
4670
|
+
out.set(file, Buffer.from(await response.arrayBuffer()));
|
|
4671
|
+
}
|
|
4672
|
+
};
|
|
4673
|
+
await Promise.all(
|
|
4674
|
+
Array.from({ length: Math.min(FETCH_CONCURRENCY, queue.length) }, () => worker())
|
|
4675
|
+
);
|
|
4676
|
+
return out;
|
|
4677
|
+
}
|
|
4678
|
+
async function assertEmptyTarget(target, id) {
|
|
4679
|
+
if (!existsSync(target)) return;
|
|
4680
|
+
const entries = await readdir(target);
|
|
4681
|
+
if (entries.length === 0) return;
|
|
4682
|
+
throw new ExampleScaffoldError(
|
|
4683
|
+
`${target} already exists and is not empty. Move it aside, or run \`pome init --example ${id}\` from a directory that does not have one.`
|
|
4684
|
+
);
|
|
4685
|
+
}
|
|
4686
|
+
async function scaffoldExample(options) {
|
|
4687
|
+
const example = findExample(options.id);
|
|
4688
|
+
if (!example) throw new ExampleScaffoldError(unknownExampleMessage(options.id));
|
|
4689
|
+
const ref = options.ref ?? resolveExampleRef();
|
|
4690
|
+
const target = join(options.cwd, example.id);
|
|
4691
|
+
await assertEmptyTarget(target, example.id);
|
|
4692
|
+
const files = await fetchExampleFiles(example, ref, options.fetchImpl ?? globalThis.fetch);
|
|
4693
|
+
for (const [file, contents] of files) {
|
|
4694
|
+
const destination = join(target, file);
|
|
4695
|
+
await mkdir(dirname(destination), { recursive: true });
|
|
4696
|
+
await writeFile(destination, contents);
|
|
4697
|
+
}
|
|
4698
|
+
return { dir: example.id, example, ref, fileCount: files.size };
|
|
4699
|
+
}
|
|
4700
|
+
function firstTaskFile(example) {
|
|
4701
|
+
return example.files.find((file) => file.startsWith("tasks/") && file.endsWith(".md"));
|
|
4702
|
+
}
|
|
4703
|
+
function scaffoldSummary(result) {
|
|
4704
|
+
const { example, dir, fileCount } = result;
|
|
4705
|
+
const header = `Scaffolded ${example.rel} into ./${dir} (${fileCount} files, from ${result.ref}).`;
|
|
4706
|
+
if (example.root === "integration-examples") {
|
|
4707
|
+
return `${header}
|
|
4708
|
+
Next steps:
|
|
4709
|
+
1. cd ${dir}
|
|
4710
|
+
2. npm install
|
|
4711
|
+
3. Read ${dir}/README.md \u2014 this example is driven by its own eval runner, not by \`pome run\`.`;
|
|
4712
|
+
}
|
|
4713
|
+
const task = firstTaskFile(example);
|
|
4714
|
+
return `${header}
|
|
4715
|
+
Next steps:
|
|
4716
|
+
1. cd ${dir}
|
|
4717
|
+
2. npm install
|
|
4718
|
+
3. pome login # one-time, opens the dashboard to sign in
|
|
4719
|
+
4. pome run ${task ?? "<task>.md"}`;
|
|
4720
|
+
}
|
|
4347
4721
|
var FIX_PROMPT_TEMPLATE_VERSION = "v1";
|
|
4348
4722
|
var MAX_EVENTS = 50;
|
|
4349
4723
|
var BODY_CHAR_LIMIT = 800;
|
|
@@ -4622,7 +4996,7 @@ var DEFAULT_AGENT_COMMAND = `node ${DEFAULT_AGENT_FILE}`;
|
|
|
4622
4996
|
var MANIFEST_SCHEMA_URL = "https://pome.sh/schemas/v1/pome.json";
|
|
4623
4997
|
var MAX_UNREADABLE_PATHS_SHOWN = 5;
|
|
4624
4998
|
function readPackageVersion() {
|
|
4625
|
-
if ("0.
|
|
4999
|
+
if ("0.35.1".length > 0) return "0.35.1";
|
|
4626
5000
|
try {
|
|
4627
5001
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
4628
5002
|
const candidates = [
|
|
@@ -4645,7 +5019,9 @@ function createProgram() {
|
|
|
4645
5019
|
program.name("pome").description(
|
|
4646
5020
|
"Test AI agents against digital twins of real SaaS APIs. Runs are recorded to app.pome.sh. Start with `pome demo`."
|
|
4647
5021
|
).version(PACKAGE_VERSION).showHelpAfterError("(add --help for usage)");
|
|
4648
|
-
program.command("init").summary("Set up Pome in this project").description(
|
|
5022
|
+
program.command("init").summary("Set up Pome in this project").description(
|
|
5023
|
+
"Create pome.json, plus starter files in a new project \u2014 or, with --example <id>, fetch a complete runnable example into ./<id>"
|
|
5024
|
+
).option(
|
|
4649
5025
|
"--sdk <name>",
|
|
4650
5026
|
"Scaffold for a specific agent SDK (claude | claude-managed). Adds the SDK-specific example file and pre-fills agent.framework so the dashboard badges runs correctly."
|
|
4651
5027
|
).option(
|
|
@@ -4654,8 +5030,38 @@ function createProgram() {
|
|
|
4654
5030
|
).option(
|
|
4655
5031
|
"--starter",
|
|
4656
5032
|
"Force the full starter library even in an existing project."
|
|
5033
|
+
).option(
|
|
5034
|
+
"--example <id>",
|
|
5035
|
+
`Scaffold a complete bundled example into ./<id> instead of a starter project. Ids: ${exampleIds().join(", ")}.`
|
|
4657
5036
|
).action(async (opts) => {
|
|
4658
5037
|
const sdk = opts.sdk?.trim();
|
|
5038
|
+
const exampleId = opts.example?.trim();
|
|
5039
|
+
if (exampleId !== void 0) {
|
|
5040
|
+
const conflicting = ["sdk", "bare", "starter"].filter(
|
|
5041
|
+
(flag) => opts[flag] !== void 0 && opts[flag] !== false
|
|
5042
|
+
);
|
|
5043
|
+
if (conflicting.length > 0) {
|
|
5044
|
+
console.error(
|
|
5045
|
+
`\`--example\` scaffolds a complete example and cannot be combined with ${conflicting.map((flag) => `--${flag}`).join(", ")}.`
|
|
5046
|
+
);
|
|
5047
|
+
process.exitCode = 2;
|
|
5048
|
+
return;
|
|
5049
|
+
}
|
|
5050
|
+
if (exampleId === "") {
|
|
5051
|
+
console.error(unknownExampleMessage(""));
|
|
5052
|
+
process.exitCode = 2;
|
|
5053
|
+
return;
|
|
5054
|
+
}
|
|
5055
|
+
try {
|
|
5056
|
+
const result = await scaffoldExample({ id: exampleId, cwd: process.cwd() });
|
|
5057
|
+
console.error(scaffoldSummary(result));
|
|
5058
|
+
} catch (err) {
|
|
5059
|
+
if (!(err instanceof ExampleScaffoldError)) throw err;
|
|
5060
|
+
console.error(err.message);
|
|
5061
|
+
process.exitCode = 2;
|
|
5062
|
+
}
|
|
5063
|
+
return;
|
|
5064
|
+
}
|
|
4659
5065
|
if (sdk !== void 0 && sdk !== "claude" && sdk !== "claude-managed") {
|
|
4660
5066
|
console.error(
|
|
4661
5067
|
`Unknown --sdk value "${opts.sdk}". Supported: claude, claude-managed.`
|