ripplo 0.4.2 → 0.4.3
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 +2 -2
- package/dist/index.js +2 -2
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ ripplo auth login # device-code flow; opens a browser
|
|
|
16
16
|
ripplo init # scaffolds .ripplo/ and writes RIPPLO_* env vars
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
`init` is interactive (or pass `--project`, `--env
|
|
19
|
+
`init` is interactive (or pass `--project`, `--env`, `--app-url`, `--engine-url`). It scaffolds `.ripplo/{index.ts, project.json, preconditions/, observers/, tests/}`, appends `RIPPLO_APP_URL` / `RIPPLO_ENGINE_URL` / `RIPPLO_WEBHOOK_SECRET` to your env file, and installs `@ripplo/testing`.
|
|
20
20
|
|
|
21
21
|
Run `ripplo watch` as a background process in your project directory — it's the local executor that subscribes to run requests and runs tests against your dev server:
|
|
22
22
|
|
|
@@ -56,7 +56,7 @@ Two things won't carry over automatically:
|
|
|
56
56
|
| `ripplo scope <sub>` | Manage testing scope (`add`, `link`, `remove`, `status`) |
|
|
57
57
|
| `ripplo flake-detect <id>` | Run a test N times in parallel to detect flakiness |
|
|
58
58
|
|
|
59
|
-
`init` flags: `--project <id> --env
|
|
59
|
+
`init` flags: `--project <id> --env <path> --app-url <url> --engine-url <url>`. `--env` is **relative to `.ripplo/`** (e.g. `../.env.local` for a repo-root file, `../apps/server/.env` for a monorepo). `--engine-url` defaults to `<app-url>/ripplo`; override it when the adapter mounts at a different prefix or your backend runs on a different port than your frontend.
|
|
60
60
|
|
|
61
61
|
## Project layout
|
|
62
62
|
|
package/dist/index.js
CHANGED
|
@@ -441,7 +441,7 @@ export const tests = [] as const;
|
|
|
441
441
|
`),process.exit(1)}return t}return`${e.replace(/\/$/,"")}/ripplo`}async function uy(e,t){if(t!=null){let r=e.find(n=>n.id===t);return r==null&&(process.stdout.write(`Unknown project id: ${t}
|
|
442
442
|
`),process.exit(1)),process.stdout.write(`Using project: ${r.name} (${r.id})
|
|
443
443
|
`),r.id}if(e.length===1){let r=e[0];if(r==null)throw new Error("unreachable");return process.stdout.write(`Using project: ${r.name} (${r.id})
|
|
444
|
-
`),r.id}return Rs({choices:e.map(r=>({name:r.name,value:r.id})),message:"Select a project"})}async function py(e,t){return t!=null?(t.trim().length===0&&(process.stdout.write(`--env
|
|
444
|
+
`),r.id}return Rs({choices:e.map(r=>({name:r.name,value:r.id})),message:"Select a project"})}async function py(e,t){return t!=null?(t.trim().length===0&&(process.stdout.write(`--env must not be empty
|
|
445
445
|
`),process.exit(1)),t):fy(e)}async function my(e){if(e!=null){try{new URL(e)}catch{process.stdout.write(`--app-url is not a valid URL: ${e}
|
|
446
446
|
`),process.exit(1)}return e}return gy()}async function fy(e){let t=We.join(e,".ripplo"),r=yn.find(o=>He.existsSync(We.resolve(t,o))),n=await Rs({choices:[...yn.map(o=>({name:r===o?`${o} (detected)`:o,value:o})),{name:"custom path",value:"__custom__"}],default:r??yn[0],message:"Which env file should ripplo write RIPPLO_APP_URL, RIPPLO_ENGINE_URL, and RIPPLO_WEBHOOK_SECRET to?"});return n!=="__custom__"?n:ks({message:"Path to env file (relative to .ripplo/, e.g. ../apps/server/.env)",validate:o=>o.trim().length>0?!0:"required"})}async function gy(){return ks({default:"http://localhost:3000",message:"Where does your dev server run? (RIPPLO_APP_URL)",validate:e=>{try{return new URL(e),!0}catch{return"must be a valid URL"}}})}function yy({appUrl:e,engineUrl:t,filePath:r}){He.mkdirSync(We.dirname(r),{recursive:!0});let n=He.existsSync(r)?He.readFileSync(r,"utf8"):"",o=[];if(/^RIPPLO_APP_URL=/m.test(n)||o.push(`RIPPLO_APP_URL=${e}`),/^RIPPLO_ENGINE_URL=/m.test(n)||o.push(`RIPPLO_ENGINE_URL=${t}`),/^RIPPLO_WEBHOOK_SECRET=/m.test(n)||o.push(`RIPPLO_WEBHOOK_SECRET=${Wr()}`),o.length===0)return;let i=n.length===0||n.endsWith(`
|
|
447
447
|
`)?"":`
|
|
@@ -667,7 +667,7 @@ ${x("create")}`}function Qh(e,t){if(t.length===0)return null;let r=ew(["run",...
|
|
|
667
667
|
`).filter(o=>/FAILED/.test(o)).join(`
|
|
668
668
|
`);return n.length===0?null:`--- Ripplo Run Failures (${t.join(" ")}) ---
|
|
669
669
|
${n}
|
|
670
|
-
Artifacts: .ripplo/debug/<runId>/. ${x("debug")}`}var Zh=Yt.object({status:Yt.number().nullish(),stderr:Yt.unknown().optional(),stdout:Yt.unknown().optional()});function fa(e){return e instanceof Buffer?e.toString("utf8"):typeof e=="string"?e:""}function ew(e,t){let r=ma.argv[1];if(r==null)return{code:1,output:""};try{return{code:0,output:Hh(ma.execPath,[r,...e],{cwd:t,encoding:"utf8",stdio:["ignore","pipe","pipe"]})}}catch(n){let o=Zh.safeParse(n);if(!o.success)return{code:1,output:""};let i=`${fa(o.data.stdout)}${fa(o.data.stderr)}`;return{code:o.data.status??1,output:i}}}import{z as ya}from"zod";var tw=ya.looseObject({skill:ya.string()}),ha=N("PostToolUse",e=>{if(e.tool_name!=="Skill")return;let t=tw.safeParse(e.tool_input);if(!t.success)return;let r=/^ripplo:(.+)$/.exec(t.data.skill);if(r==null)return;let n=r[1];n!=null&&$(e.cwd)&&ea(e.cwd,e.session_id,n)});uw();Cn(process.cwd());var wa={"coverage-nudge":Ws,"exit-plan-gate":Bs,"plan-reminder":zs,"post-edit-flag-stubs":Js,"post-edit-lint":Xs,"pre-bash-hooks-pause-gate":Zs,"pre-bash-run-gate":na,"pre-edit-ripplo-skill-gate":ia,"pre-edit-scope-gate":la,"pre-edit-watch-gate":da,"scope-reminder":ua,"session-preamble":pa,"stop-enforce":ga,"track-skill-load":ha};async function ow(){await rw(nw(process.argv)).scriptName("ripplo").command("watch","Watch for run requests and execute locally",()=>{},()=>Ns()).command("auth <subcommand>","Manage authentication",dw).command("projects <subcommand>","Inspect Ripplo projects",cw).command("hooks <subcommand>","Pause or resume Ripplo hooks",lw).command("init","Scaffold .ripplo/ in this project",e=>e.option("project",{type:"string"}).option("env
|
|
670
|
+
Artifacts: .ripplo/debug/<runId>/. ${x("debug")}`}var Zh=Yt.object({status:Yt.number().nullish(),stderr:Yt.unknown().optional(),stdout:Yt.unknown().optional()});function fa(e){return e instanceof Buffer?e.toString("utf8"):typeof e=="string"?e:""}function ew(e,t){let r=ma.argv[1];if(r==null)return{code:1,output:""};try{return{code:0,output:Hh(ma.execPath,[r,...e],{cwd:t,encoding:"utf8",stdio:["ignore","pipe","pipe"]})}}catch(n){let o=Zh.safeParse(n);if(!o.success)return{code:1,output:""};let i=`${fa(o.data.stdout)}${fa(o.data.stderr)}`;return{code:o.data.status??1,output:i}}}import{z as ya}from"zod";var tw=ya.looseObject({skill:ya.string()}),ha=N("PostToolUse",e=>{if(e.tool_name!=="Skill")return;let t=tw.safeParse(e.tool_input);if(!t.success)return;let r=/^ripplo:(.+)$/.exec(t.data.skill);if(r==null)return;let n=r[1];n!=null&&$(e.cwd)&&ea(e.cwd,e.session_id,n)});uw();Cn(process.cwd());var wa={"coverage-nudge":Ws,"exit-plan-gate":Bs,"plan-reminder":zs,"post-edit-flag-stubs":Js,"post-edit-lint":Xs,"pre-bash-hooks-pause-gate":Zs,"pre-bash-run-gate":na,"pre-edit-ripplo-skill-gate":ia,"pre-edit-scope-gate":la,"pre-edit-watch-gate":da,"scope-reminder":ua,"session-preamble":pa,"stop-enforce":ga,"track-skill-load":ha};async function ow(){await rw(nw(process.argv)).scriptName("ripplo").command("watch","Watch for run requests and execute locally",()=>{},()=>Ns()).command("auth <subcommand>","Manage authentication",dw).command("projects <subcommand>","Inspect Ripplo projects",cw).command("hooks <subcommand>","Pause or resume Ripplo hooks",lw).command("init","Scaffold .ripplo/ in this project",e=>e.option("project",{type:"string"}).option("env",{type:"string"}).option("app-url",{type:"string"}).option("engine-url",{type:"string"}),e=>xs({appUrl:e["app-url"],engineUrl:e["engine-url"],envFile:e.env,projectId:e.project})).command("run [ids..]","Run tests in parallel",e=>{let t=[];return e.positional("ids",{array:!0,default:t,describe:"Test ids to run (all if omitted)",type:"string"})},e=>As(e.ids)).command("lint [ids..]","Compile and lint tests (all or specific ids)",e=>{let t=[];return e.positional("ids",{array:!0,default:t,describe:"Test ids to lint (all if omitted)",type:"string"}).option("require-implemented",{array:!0,default:t,describe:"Test ids that must not be .notImplemented() \u2014 fails if any still are",type:"string"})},e=>Es({ids:e.ids,requireImplemented:e["require-implemented"]})).command("flake-detect <id>","Run N times to detect flakiness",e=>e.positional("id",{demandOption:!0,type:"string"}).option("runs",{default:10,type:"number"}),e=>ys({id:e.id,runs:e.runs})).command("sync","Push the compiled .ripplo/ resources to the server (no run)",()=>{},()=>$s()).command("compile","Compile the DSL and write .ripplo/ripplo.lock",e=>e.option("check",{default:!1,describe:"Exit non-zero if the lockfile is missing or stale (does not write)",type:"boolean"}),e=>Ki({check:e.check})).command("cover","Audit all coverage statements",()=>{},()=>Zi()).command("doctor","Check project health",()=>{},()=>ps()).command("status","Report unimplemented tests and preconditions",e=>e.option("format",{choices:["json","summary"],default:"json",describe:"Output format"}),e=>Us({format:e.format})).command("scope <subcommand>","Manage testing scope",aw).command("hook <name>","Internal: run a Claude Code plugin hook",e=>e.positional("name",{choices:Object.keys(wa),demandOption:!0,type:"string"}),e=>iw(e.name)).option("env",{choices:["dev","local"],describe:"Server environment",type:"string"}).strict().help().parse()}async function iw(e){let t=wa[e];t==null&&(process.stderr.write(`Unknown hook: ${e}
|
|
671
671
|
`),process.exit(1));let r=await sw(),n=r.trim()===""?{}:JSON.parse(r),o=await t.run(n);o!=null&&process.stdout.write(JSON.stringify(o))}function sw(){return new Promise((e,t)=>{if(process.stdin.isTTY){e("");return}let r=[];process.stdin.on("data",n=>r.push(n)),process.stdin.on("end",()=>{e(Buffer.concat(r).toString("utf8"))}),process.stdin.on("error",t)})}ow().catch(e=>{process.stderr.write(`${e instanceof Error?e.message:String(e)}
|
|
672
672
|
`),process.exit(1)});function aw(e){return e.command("status","Print the current scope",t=>t.option("format",{choices:["json","text"],default:"text",describe:"Output format"}),t=>Os({format:t.format})).command("add <test-ids..>","Bind one or more existing tests (stubs or implemented) to scope as agent intent",t=>{let r=[];return t.positional("test-ids",{array:!0,default:r,demandOption:!0,describe:"Slugs of existing workflows",type:"string"})},t=>Ds({testIds:t["test-ids"]})).command("link <id> <test-id>","Link an existing scope item to a test",t=>t.positional("id",{demandOption:!0,describe:"Scope item id",type:"string"}).positional("test-id",{demandOption:!0,describe:"Slug of the workflow to link",type:"string"}),t=>Ls({id:t.id,testId:t["test-id"]})).command("remove <ids..>","Remove one or more scope items by id",t=>{let r=[];return t.positional("ids",{array:!0,default:r,demandOption:!0,describe:"Scope item ids",type:"string"})},t=>_s({ids:t.ids})).demandCommand(1)}function lw(e){return e.command("pause","Disable all Ripplo pre-edit gates and stop enforcement until resumed",()=>{},()=>hs()).command("resume","Re-enable Ripplo hooks paused via `ripplo hooks pause`",()=>{},()=>ws()).demandCommand(1)}function cw(e){return e.command("list","List projects you have access to (JSON)",()=>{},()=>Ts()).demandCommand(1)}function dw(e){return e.command("login","Authenticate via device flow",()=>{},()=>ki()).command("status","Show authentication status",()=>{},()=>Ri()).command("logout","Remove the saved token",()=>{},()=>{xi()}).demandCommand(1)}function uw(){let e=process.cwd(),t=Xt(e);t!=null&&t!==e&&(process.chdir(t),process.stderr.write(`ripplo: resolved .ripplo/ at ${t}
|
|
673
673
|
`))}export{ow as main};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ripplo",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.3",
|
|
4
4
|
"description": "CLI for Ripplo — AI-powered end-to-end testing",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"homepage": "https://ripplo.ai",
|
|
@@ -47,10 +47,10 @@
|
|
|
47
47
|
"eslint": "catalog:",
|
|
48
48
|
"tsup": "^8.5.1",
|
|
49
49
|
"@ripplo/eslint-config": "0.0.0",
|
|
50
|
-
"@ripplo/
|
|
50
|
+
"@ripplo/testing": "^0.4.2",
|
|
51
51
|
"@ripplo/runtime": "^0.0.0",
|
|
52
52
|
"@ripplo/spec": "^0.0.0",
|
|
53
|
-
"@ripplo/
|
|
53
|
+
"@ripplo/graphql": "^0.0.0"
|
|
54
54
|
},
|
|
55
55
|
"scripts": {
|
|
56
56
|
"dev": "tsx watch src/index.ts",
|