@sente-labs/cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,90 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.loadCredentials = loadCredentials;
37
+ exports.saveCredentials = saveCredentials;
38
+ exports.clearCredentials = clearCredentials;
39
+ const fs = __importStar(require("fs"));
40
+ const config_1 = require("./config");
41
+ /**
42
+ * Read credentials from disk. Returns null if the file doesn't exist or is malformed.
43
+ * Env vars override the file contents -- the SENTE_API_TOKEN env var always wins.
44
+ */
45
+ function loadCredentials() {
46
+ // Env override path: build credentials object from env vars; ignore disk.
47
+ const envT = (0, config_1.envToken)();
48
+ if (envT) {
49
+ return {
50
+ token: envT,
51
+ gatewayUrl: (0, config_1.gatewayUrl)(),
52
+ };
53
+ }
54
+ try {
55
+ const raw = fs.readFileSync(config_1.CREDENTIALS_PATH, 'utf8');
56
+ const parsed = JSON.parse(raw);
57
+ if (typeof parsed.token !== 'string' || parsed.token.length === 0) {
58
+ return null;
59
+ }
60
+ // Env can still override gatewayUrl for the same stored token.
61
+ return { ...parsed, gatewayUrl: (0, config_1.gatewayUrl)() };
62
+ }
63
+ catch {
64
+ return null;
65
+ }
66
+ }
67
+ /**
68
+ * Write credentials atomically with 0600 permissions.
69
+ * Creates ~/.sente if missing.
70
+ */
71
+ function saveCredentials(creds) {
72
+ if (!fs.existsSync(config_1.CREDENTIALS_DIR)) {
73
+ fs.mkdirSync(config_1.CREDENTIALS_DIR, { recursive: true, mode: 0o700 });
74
+ }
75
+ const tmp = `${config_1.CREDENTIALS_PATH}.tmp`;
76
+ fs.writeFileSync(tmp, JSON.stringify(creds, null, 2), { mode: 0o600 });
77
+ fs.renameSync(tmp, config_1.CREDENTIALS_PATH);
78
+ }
79
+ /**
80
+ * Remove stored credentials. No-op if the file doesn't exist.
81
+ */
82
+ function clearCredentials() {
83
+ try {
84
+ fs.unlinkSync(config_1.CREDENTIALS_PATH);
85
+ }
86
+ catch {
87
+ // ignore
88
+ }
89
+ }
90
+ //# sourceMappingURL=credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials.js","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAeA,0CAqBC;AAMD,0CAOC;AAKD,4CAMC;AA5DD,uCAAyB;AACzB,qCAAmF;AAUnF;;;GAGG;AACH,SAAgB,eAAe;IAC7B,0EAA0E;IAC1E,MAAM,IAAI,GAAG,IAAA,iBAAQ,GAAE,CAAC;IACxB,IAAI,IAAI,EAAE,CAAC;QACT,OAAO;YACL,KAAK,EAAE,IAAI;YACX,UAAU,EAAE,IAAA,mBAAU,GAAE;SACzB,CAAC;IACJ,CAAC;IAED,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,yBAAgB,EAAE,MAAM,CAAC,CAAC;QACtD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAgB,CAAC;QAC9C,IAAI,OAAO,MAAM,CAAC,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAClE,OAAO,IAAI,CAAC;QACd,CAAC;QACD,+DAA+D;QAC/D,OAAO,EAAE,GAAG,MAAM,EAAE,UAAU,EAAE,IAAA,mBAAU,GAAE,EAAE,CAAC;IACjD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAgB,eAAe,CAAC,KAAkB;IAChD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,wBAAe,CAAC,EAAE,CAAC;QACpC,EAAE,CAAC,SAAS,CAAC,wBAAe,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAClE,CAAC;IACD,MAAM,GAAG,GAAG,GAAG,yBAAgB,MAAM,CAAC;IACtC,EAAE,CAAC,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACvE,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,yBAAgB,CAAC,CAAC;AACvC,CAAC;AAED;;GAEG;AACH,SAAgB,gBAAgB;IAC9B,IAAI,CAAC;QACH,EAAE,CAAC,UAAU,CAAC,yBAAgB,CAAC,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,SAAS;IACX,CAAC;AACH,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const commander_1 = require("commander");
5
+ const login_1 = require("./commands/login");
6
+ const whoami_1 = require("./commands/whoami");
7
+ const project_1 = require("./commands/project");
8
+ const task_1 = require("./commands/task");
9
+ const init_1 = require("./commands/init");
10
+ const program = new commander_1.Command();
11
+ program
12
+ .name('sente')
13
+ .description('Sente CLI -- manage QA tests from the command line and Claude Code')
14
+ .version('0.1.0')
15
+ .option('--json', 'Emit machine-readable JSON instead of human output');
16
+ /**
17
+ * Global options aren't auto-propagated to subcommands by Commander, so we
18
+ * read them off the top-level program at action time. Subcommands call this
19
+ * accessor inside their action handler.
20
+ */
21
+ const globalOpts = () => program.opts();
22
+ (0, login_1.registerLoginCommand)(program, globalOpts);
23
+ (0, whoami_1.registerWhoamiCommand)(program, globalOpts);
24
+ (0, init_1.registerInitCommand)(program, globalOpts);
25
+ (0, init_1.registerInstallSkillCommand)(program, globalOpts);
26
+ (0, project_1.registerProjectCommands)(program, globalOpts);
27
+ (0, project_1.registerSyncCommand)(program, globalOpts);
28
+ (0, task_1.registerTaskCommands)(program, globalOpts);
29
+ program.parseAsync(process.argv).catch((err) => {
30
+ // Defensive: command actions handle their own errors. This only catches
31
+ // setup-time errors (e.g. unknown subcommand).
32
+ process.stderr.write(`Error: ${err?.message ?? err}\n`);
33
+ process.exit(1);
34
+ });
35
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AACA,yCAAoC;AACpC,4CAAwD;AACxD,8CAA0D;AAC1D,gDAAkF;AAClF,0CAAuD;AACvD,0CAAmF;AAGnF,MAAM,OAAO,GAAG,IAAI,mBAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,OAAO,CAAC;KACb,WAAW,CAAC,oEAAoE,CAAC;KACjF,OAAO,CAAC,OAAO,CAAC;KAChB,MAAM,CAAC,QAAQ,EAAE,oDAAoD,CAAC,CAAC;AAE1E;;;;GAIG;AACH,MAAM,UAAU,GAAG,GAAe,EAAE,CAAC,OAAO,CAAC,IAAI,EAAc,CAAC;AAEhE,IAAA,4BAAoB,EAAC,OAAO,EAAE,UAAU,CAAC,CAAC;AAC1C,IAAA,8BAAqB,EAAC,OAAO,EAAE,UAAU,CAAC,CAAC;AAC3C,IAAA,0BAAmB,EAAC,OAAO,EAAE,UAAU,CAAC,CAAC;AACzC,IAAA,kCAA2B,EAAC,OAAO,EAAE,UAAU,CAAC,CAAC;AACjD,IAAA,iCAAuB,EAAC,OAAO,EAAE,UAAU,CAAC,CAAC;AAC7C,IAAA,6BAAmB,EAAC,OAAO,EAAE,UAAU,CAAC,CAAC;AACzC,IAAA,2BAAoB,EAAC,OAAO,EAAE,UAAU,CAAC,CAAC;AAE1C,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IAC7C,wEAAwE;IACxE,+CAA+C;IAC/C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,GAAG,EAAE,OAAO,IAAI,GAAG,IAAI,CAAC,CAAC;IACxD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
package/dist/output.js ADDED
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ /**
3
+ * Output helpers. Every command supports `--json` for machine consumption;
4
+ * the default is a human-readable rendering. Claude Code always uses --json.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.isJson = isJson;
8
+ exports.printSuccess = printSuccess;
9
+ exports.printError = printError;
10
+ function isJson(opts) {
11
+ return !!opts.json;
12
+ }
13
+ /**
14
+ * Print success data: JSON if --json, otherwise let the caller format.
15
+ */
16
+ function printSuccess(data, human, opts) {
17
+ if (isJson(opts)) {
18
+ process.stdout.write(JSON.stringify(data, null, 2) + '\n');
19
+ return;
20
+ }
21
+ if (typeof human === 'string') {
22
+ process.stdout.write(human + (human.endsWith('\n') ? '' : '\n'));
23
+ }
24
+ else {
25
+ human();
26
+ }
27
+ }
28
+ /**
29
+ * Print an error. Always to stderr. JSON mode includes any structured body
30
+ * the server returned (e.g. STEP_VALIDATION_FAILED's errors[] + warnings[])
31
+ * so machine consumers can act on it.
32
+ * Returns the exit code the caller should use.
33
+ */
34
+ function printError(err, opts) {
35
+ const message = err instanceof Error ? err.message : String(err);
36
+ const code = err?.code ?? 'ERROR';
37
+ if (isJson(opts)) {
38
+ const body = err?.body;
39
+ const payload = { error: message, code };
40
+ // Surface structured errors/warnings the server attached (e.g. validation).
41
+ if (body && typeof body === 'object') {
42
+ if (Array.isArray(body.errors))
43
+ payload.errors = body.errors;
44
+ if (Array.isArray(body.warnings))
45
+ payload.warnings = body.warnings;
46
+ }
47
+ process.stderr.write(JSON.stringify(payload) + '\n');
48
+ }
49
+ else {
50
+ process.stderr.write(`Error: ${message}\n`);
51
+ }
52
+ return 1;
53
+ }
54
+ //# sourceMappingURL=output.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output.js","sourceRoot":"","sources":["../src/output.ts"],"names":[],"mappings":";AAAA;;;GAGG;;AAMH,wBAEC;AAKD,oCAUC;AAQD,gCAgBC;AAzCD,SAAgB,MAAM,CAAC,IAAgB;IACrC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;AACrB,CAAC;AAED;;GAEG;AACH,SAAgB,YAAY,CAAC,IAAa,EAAE,KAA4B,EAAE,IAAgB;IACxF,IAAI,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;QACjB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;QAC3D,OAAO;IACT,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IACnE,CAAC;SAAM,CAAC;QACN,KAAK,EAAE,CAAC;IACV,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,SAAgB,UAAU,CAAC,GAAY,EAAE,IAAgB;IACvD,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACjE,MAAM,IAAI,GAAI,GAAW,EAAE,IAAI,IAAI,OAAO,CAAC;IAC3C,IAAI,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;QACjB,MAAM,IAAI,GAAI,GAAW,EAAE,IAAI,CAAC;QAChC,MAAM,OAAO,GAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QAC9C,4EAA4E;QAC5E,IAAI,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;YACrC,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC;gBAAE,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;YAC7D,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC;gBAAE,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;QACrE,CAAC;QACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;IACvD,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,OAAO,IAAI,CAAC,CAAC;IAC9C,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC"}
@@ -0,0 +1,116 @@
1
+ ---
2
+ name: sente
3
+ description: Author and update Sente QA tests for the user's app from within Claude Code. Use when the user finishes a feature, prepares a commit or PR, debugs a failing Sente test, references a Sente task, or mentions "Sente". Drafts well-structured action/assertion steps from the repo context and creates them via the `sente` CLI; reads run results to distinguish app regressions from test issues.
4
+ ---
5
+
6
+ # Sente
7
+
8
+ Sente runs QA tests against the user's deployed staging environment via a cloud-hosted Chrome browser. This skill lets you author and maintain those tests from inside Claude Code using the `sente` CLI.
9
+
10
+ ## When to use
11
+
12
+ - The user just finished a feature, is preparing a commit or PR, or asks "is this covered by a test?"
13
+ - A Sente run failed and the user is debugging it (or pastes a failure report/Slack message into the chat)
14
+ - The user mentions "Sente", references a Sente task id, or asks about test coverage
15
+
16
+ **Do not volunteer on every file save.** Only at meaningful milestones (feature complete, commit, PR open, or explicit ask). Use the `.sente/last-checked.json` hash file if available to avoid re-evaluating unchanged paths.
17
+
18
+ ## Critical context
19
+
20
+ - Sente runs against **publicly-reachable URLs only** (cloud browser). Local dev / non-public PR previews are not supported. Always test against staging.
21
+ - Tests run **after the next staging deploy**, not when you create them. **Do not call `sente task run` immediately after `sente task create`** — staging probably doesn't have the new code yet. If the user wants to run now, confirm the deploy landed.
22
+ - Project context lives in `.sente/config.json` (`{ projectId, gatewayUrl, baseUrl }`). The task-to-paths map lives in `.sente/tasks.json`.
23
+
24
+ ## Workflow recipes
25
+
26
+ ### A. Creating a new test
27
+
28
+ 1. Read `.sente/config.json` and `.sente/tasks.json` to confirm the project exists and to learn the staging baseUrl.
29
+ 2. Identify the workflow id to attach the task to — list with `sente workflow list --json` (when available; until then ask the user or `sente task list --json` to infer from existing tasks).
30
+ 3. Draft steps following the **Test Authoring Rubric** below.
31
+ 4. Write the steps to a temp file (e.g., `/tmp/sente-steps-<timestamp>.json`).
32
+ 5. Call `sente task create --workflow <id> --name "..." --instruction "..." --steps-from <file> --json`.
33
+ 6. If validation errors come back, fix the steps and retry. If only warnings, surface them to the user but proceed.
34
+ 7. Update `.sente/tasks.json` with the new task entry (id, name, optional `tags` and `relatedPaths`).
35
+ 8. **Do not run the test now** — explain that the test runs on the next staging deploy.
36
+
37
+ ### B. Updating an existing test
38
+
39
+ 1. Identify the task using `.sente/tasks.json` (match by `relatedPaths` globs against the code the user just touched, or by name).
40
+ 2. Read the current state with `sente task show <id> --json`.
41
+ 3. Propose the changes to the user before writing. Edit only the steps that need updating; preserve the rest.
42
+ 4. Write the full updated step array to a temp file.
43
+ 5. Call `sente task update <id> --steps-from <file> --json`.
44
+ 6. Surface warnings.
45
+
46
+ ### C. Diagnosing a failure
47
+
48
+ When the user pastes a failure or asks why a run failed:
49
+
50
+ 1. `sente task results <id> --failed-step --json` to get the focused payload.
51
+ 2. Read `failureCategory` and `suggestedFix`:
52
+ - **`regression`**: the app is broken. Stop, summarize what failed in plain English, and ask whether to file a bug. Do NOT auto-edit the test.
53
+ - **`test_design`**: the test is wrong for the current app. Propose a fix to the steps, then update the task using recipe B.
54
+ 3. If the failure is ambiguous, fetch full details with `sente task show <id> --json` and inspect step-by-step `actual` vs `assertion`.
55
+
56
+ ## Test Authoring Rubric
57
+
58
+ The agent that runs your tests enforces this style. Steps that follow it pass more reliably.
59
+
60
+ ### Core principle
61
+
62
+ **Be specific about DATA and OUTCOMES, flexible about UI ELEMENTS.** Button labels and exact text may change; data and observable outcomes should not.
63
+
64
+ ### Per step
65
+
66
+ - **action**: one verb, specific data, user-visible language.
67
+ - Good: `Log in with email 'qa@acme.com' and password {{secret.qaPassword}}`
68
+ - Good: `Navigate to https://staging.myapp.com/settings/billing`
69
+ - Bad: `Click the button with id='submit-btn'` (UI-rigid)
70
+ - Bad: `Enter text in the third input field` (positional)
71
+ - Bad: `Submit the form` (vague — which form?)
72
+
73
+ - **assertion**: describe the DOM text change after the action. What text appears, disappears, or changes.
74
+ - Good: `Cart count changes from "0 items" to "1 item"`
75
+ - Good: `Error message appears stating credentials are invalid` (semantic, not exact wording)
76
+ - Good: `Dashboard URL shown with the user's email in the top navigation`
77
+ - Bad: `Verify text "Welcome back, John!" appears` (exact wording rigid)
78
+ - Bad: `The save succeeds` (no observable)
79
+ - Counts and existence are LITERAL: `"no results"` means zero, not "22 unrelated".
80
+
81
+ ### Hard rules
82
+
83
+ - **Secrets** use literal placeholder syntax: `{{secret.name_here}}`. Never embed values. List existing secrets with `sente secret list --json` (when available).
84
+ - **Never invent dropdown values**, product names, or emails. If you don't know, ask the user.
85
+ - **Combine related micro-actions into one step** (form fill + submit = one step).
86
+ - **At most 20 steps per task.** If a flow needs more, split into multiple tasks with a precondition note in the second one's instruction.
87
+ - **Cleanup**: if the test creates data, the last step deletes it.
88
+ - **Use full URLs** when navigating to a known page (prefer `goto` over click-trails).
89
+
90
+ ### Task-level instruction
91
+
92
+ The top-level `--instruction` is consulted by the agent when step text is ambiguous. Keep it detailed about the specific UI mechanism: *"Click Add Contact and fill in the manual form"* — not *"add a contact somehow"*. The instruction wins when steps and instruction disagree.
93
+
94
+ ## Command reference
95
+
96
+ | Command | Purpose |
97
+ |---|---|
98
+ | `sente whoami --json` | Confirm auth and current org |
99
+ | `sente project show --json` | Show the project bound to this repo |
100
+ | `sente task list --json` | List tasks (optionally `--project <id>`) |
101
+ | `sente task show <id> --json` | Full task details + steps |
102
+ | `sente task create --workflow <id> --name "..." --instruction "..." --steps-from <file> --json` | Create a new QA task |
103
+ | `sente task update <id> --steps-from <file> --json` | Replace steps; also accepts `--name`, `--instruction`, `--description` |
104
+ | `sente task delete <id> --json` | Delete a task |
105
+ | `sente task results <id> --failed-step --json` | Focused failure payload for diagnosis |
106
+ | `sente task results <id> --json` | Full step-by-step results |
107
+ | `sente sync --json` | Re-pull tasks into `.sente/tasks.json` |
108
+
109
+ Use `--json` for every CLI call — outputs are stable JSON shaped for programmatic consumption.
110
+
111
+ ## Error handling
112
+
113
+ - `STEP_VALIDATION_FAILED` (HTTP 400): the `errors[]` array tells you exactly what to fix. Apply the fix to the steps file and retry.
114
+ - `UNKNOWN_SECRET`: the referenced secret doesn't exist in this organization. Ask the user to create it via the web UI, or use a different secret.
115
+ - `INVALID_PAT`: the stored PAT is bad. Ask the user to re-run `sente login` after issuing a new token.
116
+ - `NETWORK_ERROR`: the gateway is unreachable. Surface the host to the user.
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.SKILL_MD = void 0;
37
+ exports.skillInstallPath = skillInstallPath;
38
+ const fs = __importStar(require("fs"));
39
+ const path = __importStar(require("path"));
40
+ /**
41
+ * Bundled SKILL.md content. The .md file lives next to this module both at
42
+ * dev time (src/skill/) and at runtime (dist/skill/, copied by the build script).
43
+ *
44
+ * Read once at module load so callers can pass the content around freely.
45
+ */
46
+ exports.SKILL_MD = fs.readFileSync(path.join(__dirname, 'SKILL.md'), 'utf8');
47
+ /**
48
+ * Where the skill gets installed for Claude Code to discover it.
49
+ * Path is the standard convention documented at https://code.claude.com/docs/en/skills.
50
+ */
51
+ function skillInstallPath(homeDir) {
52
+ return path.join(homeDir, '.claude', 'skills', 'sente', 'SKILL.md');
53
+ }
54
+ //# sourceMappingURL=template.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template.js","sourceRoot":"","sources":["../../src/skill/template.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkBA,4CAEC;AApBD,uCAAyB;AACzB,2CAA6B;AAE7B;;;;;GAKG;AACU,QAAA,QAAQ,GAAW,EAAE,CAAC,YAAY,CAC7C,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC,EAChC,MAAM,CACP,CAAC;AAEF;;;GAGG;AACH,SAAgB,gBAAgB,CAAC,OAAe;IAC9C,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC;AACtE,CAAC"}
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@sente-labs/cli",
3
+ "version": "0.1.0",
4
+ "description": "Sente CLI -- author and manage QA tests from your terminal and Claude Code. Tests run against your staging URL after each deploy.",
5
+ "bin": {
6
+ "sente": "dist/index.js"
7
+ },
8
+ "main": "dist/index.js",
9
+ "files": [
10
+ "dist",
11
+ "README.md"
12
+ ],
13
+ "scripts": {
14
+ "build": "tsc && mkdir -p dist/skill && cp src/skill/SKILL.md dist/skill/SKILL.md && chmod +x dist/index.js",
15
+ "dev": "ts-node src/index.ts",
16
+ "typecheck": "tsc --noEmit",
17
+ "prepublishOnly": "npm run build",
18
+ "clean": "rm -rf dist"
19
+ },
20
+ "engines": {
21
+ "node": ">=18"
22
+ },
23
+ "keywords": [
24
+ "sente",
25
+ "qa",
26
+ "testing",
27
+ "claude-code",
28
+ "browser-automation",
29
+ "e2e",
30
+ "staging",
31
+ "cli"
32
+ ],
33
+ "homepage": "https://sente.run",
34
+ "bugs": {
35
+ "url": "https://github.com/sente-labs/agentic-browser/issues"
36
+ },
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "git+https://github.com/sente-labs/agentic-browser.git",
40
+ "directory": "cli"
41
+ },
42
+ "publishConfig": {
43
+ "access": "public"
44
+ },
45
+ "dependencies": {
46
+ "commander": "^12.0.0"
47
+ },
48
+ "devDependencies": {
49
+ "@types/node": "^20.0.0",
50
+ "ts-node": "^10.9.0",
51
+ "typescript": "^5.0.0"
52
+ },
53
+ "license": "UNLICENSED"
54
+ }