@sente-labs/cli 0.2.1 → 0.4.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.
package/dist/client.js CHANGED
@@ -45,7 +45,7 @@ class GatewayClient {
45
45
  headers: {
46
46
  Authorization: `Bearer ${this.creds.token}`,
47
47
  'Content-Type': 'application/json',
48
- 'User-Agent': 'sente-cli/0.2.1',
48
+ 'User-Agent': 'sente-cli/0.4.0',
49
49
  },
50
50
  };
51
51
  if (opts.body !== undefined) {
@@ -0,0 +1,188 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerScheduleCommands = registerScheduleCommands;
4
+ const client_1 = require("../client");
5
+ const output_1 = require("../output");
6
+ /**
7
+ * Parse a human-friendly interval string ("30m", "1h", "2d", "1w") into the
8
+ * gateway's `{intervalValue, intervalUnit}` shape. Accepts bare units too:
9
+ * "minute(s)", "hour(s)", "day(s)", "week(s)" -- always lower-case.
10
+ */
11
+ function parseInterval(raw) {
12
+ const trimmed = raw.trim().toLowerCase();
13
+ // Shorthand first: "30m", "1h", "2d", "1w".
14
+ const shortMatch = trimmed.match(/^(\d+)\s*([smhdw])$/);
15
+ if (shortMatch) {
16
+ const value = parseInt(shortMatch[1], 10);
17
+ const u = shortMatch[2];
18
+ const unit = u === 'm' ? 'minutes' : u === 'h' ? 'hours' : u === 'd' ? 'days' : u === 'w' ? 'weeks' : null;
19
+ if (unit && value > 0)
20
+ return { intervalValue: value, intervalUnit: unit };
21
+ }
22
+ // Long form: "5 minutes", "1 hour", "3 days", "2 weeks".
23
+ const longMatch = trimmed.match(/^(\d+)\s*(minute|minutes|hour|hours|day|days|week|weeks)$/);
24
+ if (longMatch) {
25
+ const value = parseInt(longMatch[1], 10);
26
+ const u = longMatch[2];
27
+ const unit = u.startsWith('minute') ? 'minutes' : u.startsWith('hour') ? 'hours' : u.startsWith('day') ? 'days' : 'weeks';
28
+ if (value > 0)
29
+ return { intervalValue: value, intervalUnit: unit };
30
+ }
31
+ throw new Error(`Could not parse interval "${raw}". Use e.g. "30m", "1h", "2d", "1w" or "5 minutes", "1 hour".`);
32
+ }
33
+ /**
34
+ * Translate CLI flags into the gateway's schedule body. Exactly one of
35
+ * `--interval`, `--cron`, `--at` must be set.
36
+ */
37
+ function buildScheduleBody(opts) {
38
+ const provided = [opts.interval, opts.cron, opts.at].filter(Boolean).length;
39
+ if (provided === 0) {
40
+ throw new Error('Pass exactly one of --interval, --cron, or --at.');
41
+ }
42
+ if (provided > 1) {
43
+ throw new Error('--interval, --cron, and --at are mutually exclusive.');
44
+ }
45
+ const enabled = !opts.disabled;
46
+ const timezone = opts.timezone ?? 'UTC';
47
+ if (opts.interval) {
48
+ const { intervalValue, intervalUnit } = parseInterval(opts.interval);
49
+ return { scheduleType: 'interval', intervalValue, intervalUnit, timezone, enabled };
50
+ }
51
+ if (opts.cron) {
52
+ return { scheduleType: 'cron', cronExpression: opts.cron, timezone, enabled };
53
+ }
54
+ // one_time
55
+ const date = new Date(opts.at);
56
+ if (Number.isNaN(date.getTime())) {
57
+ throw new Error(`Could not parse --at "${opts.at}". Use ISO 8601 (e.g. "2026-12-31T17:00:00Z").`);
58
+ }
59
+ return { scheduleType: 'one_time', scheduledAt: date.toISOString(), timezone, enabled };
60
+ }
61
+ function describeSchedule(s) {
62
+ switch (s.scheduleType) {
63
+ case 'interval':
64
+ return `every ${s.intervalValue} ${s.intervalUnit}`;
65
+ case 'cron':
66
+ return `cron "${s.cronExpression}" (${s.timezone})`;
67
+ case 'one_time':
68
+ return `once at ${s.scheduledAt}`;
69
+ }
70
+ }
71
+ function registerScheduleCommands(program, globalOpts) {
72
+ const schedule = program.command('schedule').description('Task schedule commands');
73
+ schedule
74
+ .command('set')
75
+ .description('Create or replace a task schedule')
76
+ .argument('<taskId>', 'task id (or shortId)')
77
+ .option('--interval <duration>', 'recurring interval (e.g. 30m, 1h, 2d, 1w)')
78
+ .option('--cron <expression>', 'cron expression (e.g. "0 */2 * * *")')
79
+ .option('--at <iso>', 'run once at this ISO 8601 datetime')
80
+ .option('--timezone <tz>', 'IANA timezone for cron schedules', 'UTC')
81
+ .option('--disabled', 'create the schedule but leave it disabled')
82
+ .action(async (taskId, cmdOpts) => {
83
+ const opts = globalOpts();
84
+ try {
85
+ const body = buildScheduleBody(cmdOpts);
86
+ const client = new client_1.GatewayClient();
87
+ // POST creates, PUT updates. Try create first; on 409 fall back to update.
88
+ let res;
89
+ try {
90
+ res = await client.request(`/api/tasks/${taskId}/schedule`, {
91
+ method: 'POST',
92
+ body,
93
+ });
94
+ }
95
+ catch (err) {
96
+ if (err instanceof client_1.ApiError && err.status === 409) {
97
+ res = await client.request(`/api/tasks/${taskId}/schedule`, {
98
+ method: 'PUT',
99
+ body,
100
+ });
101
+ }
102
+ else {
103
+ throw err;
104
+ }
105
+ }
106
+ (0, output_1.printSuccess)({ schedule: res.schedule }, () => {
107
+ const s = res.schedule;
108
+ process.stdout.write(`Schedule ${s.id}: ${describeSchedule(s)}${s.enabled ? '' : ' (disabled)'}\n`);
109
+ if (s.nextRunAt) {
110
+ process.stdout.write(`Next run: ${s.nextRunAt}\n`);
111
+ }
112
+ }, opts);
113
+ }
114
+ catch (err) {
115
+ process.exit((0, output_1.printError)(err, opts));
116
+ }
117
+ });
118
+ schedule
119
+ .command('show')
120
+ .description('Show the schedule attached to a task (if any)')
121
+ .argument('<taskId>', 'task id')
122
+ .action(async (taskId) => {
123
+ const opts = globalOpts();
124
+ try {
125
+ const client = new client_1.GatewayClient();
126
+ const res = await client.request(`/api/tasks/${taskId}/schedule`);
127
+ (0, output_1.printSuccess)({ schedule: res.schedule }, () => {
128
+ if (!res.schedule) {
129
+ process.stdout.write('No schedule attached.\n');
130
+ return;
131
+ }
132
+ const s = res.schedule;
133
+ process.stdout.write(`${describeSchedule(s)}${s.enabled ? '' : ' (disabled)'}\n`);
134
+ if (s.nextRunAt)
135
+ process.stdout.write(`Next run: ${s.nextRunAt}\n`);
136
+ if (s.lastRunAt) {
137
+ process.stdout.write(`Last run: ${s.lastRunAt}${s.lastRunStatus ? ` (${s.lastRunStatus})` : ''}\n`);
138
+ }
139
+ }, opts);
140
+ }
141
+ catch (err) {
142
+ process.exit((0, output_1.printError)(err, opts));
143
+ }
144
+ });
145
+ schedule
146
+ .command('remove')
147
+ .description('Remove the schedule attached to a task')
148
+ .argument('<taskId>', 'task id')
149
+ .action(async (taskId) => {
150
+ const opts = globalOpts();
151
+ try {
152
+ const client = new client_1.GatewayClient();
153
+ await client.request(`/api/tasks/${taskId}/schedule`, { method: 'DELETE' });
154
+ (0, output_1.printSuccess)({ ok: true, taskId }, `Removed schedule from task ${taskId}`, opts);
155
+ }
156
+ catch (err) {
157
+ process.exit((0, output_1.printError)(err, opts));
158
+ }
159
+ });
160
+ schedule
161
+ .command('list')
162
+ .description('List scheduled tasks in the current organization')
163
+ .option('--limit <n>', 'max rows (default 10, max 50)', '10')
164
+ .action(async (cmdOpts) => {
165
+ const opts = globalOpts();
166
+ try {
167
+ const limit = Math.max(1, Math.min(50, parseInt(cmdOpts.limit, 10) || 10));
168
+ const client = new client_1.GatewayClient();
169
+ const res = await client.request('/api/tasks/scheduled', {
170
+ query: { limit },
171
+ });
172
+ (0, output_1.printSuccess)({ total: res.total, schedules: res.schedules }, () => {
173
+ if (res.schedules.length === 0) {
174
+ process.stdout.write('No scheduled tasks.\n');
175
+ return;
176
+ }
177
+ for (const s of res.schedules) {
178
+ const line = `${s.taskId} ${describeSchedule(s.schedule).padEnd(28)} ${s.projectName} / ${s.workflowName} / ${s.taskName}`;
179
+ process.stdout.write(`${line}\n`);
180
+ }
181
+ }, opts);
182
+ }
183
+ catch (err) {
184
+ process.exit((0, output_1.printError)(err, opts));
185
+ }
186
+ });
187
+ }
188
+ //# sourceMappingURL=schedule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schedule.js","sourceRoot":"","sources":["../../src/commands/schedule.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -8,11 +8,21 @@ const project_1 = require("./commands/project");
8
8
  const task_1 = require("./commands/task");
9
9
  const init_1 = require("./commands/init");
10
10
  const secret_1 = require("./commands/secret");
11
+ const schedule_1 = require("./commands/schedule");
12
+ const autoupdate_1 = require("./skill/autoupdate");
13
+ // Silently refresh the installed Claude Code skill to match the running CLI.
14
+ // Synchronous + cheap (a file stat + at most one small write). No-op if the
15
+ // user customized the skill (no version marker) or --json mode is active.
16
+ (0, autoupdate_1.autoUpdateSkillIfStale)();
17
+ // Fire-and-forget npm registry check. Cached for 24h. Prints a one-line
18
+ // nudge to stderr if a newer CLI is available. Never blocks the user's
19
+ // command; settles at most by the time their action returns.
20
+ void (0, autoupdate_1.checkForCliUpdate)();
11
21
  const program = new commander_1.Command();
12
22
  program
13
23
  .name('sente')
14
24
  .description('Sente CLI -- manage QA tests from the command line and Claude Code')
15
- .version('0.2.1')
25
+ .version('0.4.0')
16
26
  .option('--json', 'Emit machine-readable JSON instead of human output');
17
27
  /**
18
28
  * Global options aren't auto-propagated to subcommands by Commander, so we
@@ -28,6 +38,7 @@ const globalOpts = () => program.opts();
28
38
  (0, project_1.registerSyncCommand)(program, globalOpts);
29
39
  (0, task_1.registerTaskCommands)(program, globalOpts);
30
40
  (0, secret_1.registerSecretCommands)(program, globalOpts);
41
+ (0, schedule_1.registerScheduleCommands)(program, globalOpts);
31
42
  program.parseAsync(process.argv).catch((err) => {
32
43
  // Defensive: command actions handle their own errors. This only catches
33
44
  // setup-time errors (e.g. unknown subcommand).
package/dist/index.js.map CHANGED
@@ -1 +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;AACnF,8CAA2D;AAG3D,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;AAC1C,IAAA,+BAAsB,EAAC,OAAO,EAAE,UAAU,CAAC,CAAC;AAE5C,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"}
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;AACnF,8CAA2D;AAC3D,kDAA+D;AAC/D,mDAA+E;AAG/E,6EAA6E;AAC7E,4EAA4E;AAC5E,0EAA0E;AAC1E,IAAA,mCAAsB,GAAE,CAAC;AAEzB,wEAAwE;AACxE,uEAAuE;AACvE,6DAA6D;AAC7D,KAAK,IAAA,8BAAiB,GAAE,CAAC;AAEzB,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;AAC1C,IAAA,+BAAsB,EAAC,OAAO,EAAE,UAAU,CAAC,CAAC;AAC5C,IAAA,mCAAwB,EAAC,OAAO,EAAE,UAAU,CAAC,CAAC;AAE9C,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"}
@@ -3,6 +3,8 @@ name: sente
3
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
4
  ---
5
5
 
6
+ <!-- sente-cli-version: __CLI_VERSION__ -- auto-managed by the sente CLI. If you remove this line, the CLI will stop auto-updating this file. -->
7
+
6
8
  # Sente
7
9
 
8
10
  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.
@@ -109,6 +111,10 @@ The top-level `--instruction` is consulted by the agent when step text is ambigu
109
111
  | `sente secret create --name <key> --project <projectId> [--target-url <url>]` | Create a secret. Hidden prompt for value, or `--value-stdin` for piping |
110
112
  | `sente secret link <secretId> <projectId>` / `unlink ...` | Link/unlink an existing secret to a project |
111
113
  | `sente secret delete <secretId>` | Delete a secret (removes from all linked projects) |
114
+ | `sente schedule set <taskId> --interval 1h \| --cron "..." \| --at <iso>` | Attach a schedule to a task (only one of the three) |
115
+ | `sente schedule show <taskId>` | Show a task's schedule (if any) |
116
+ | `sente schedule remove <taskId>` | Remove a task's schedule |
117
+ | `sente schedule list --json` | List scheduled tasks in the current org |
112
118
 
113
119
  Use `--json` for every CLI call — outputs are stable JSON shaped for programmatic consumption.
114
120
 
@@ -0,0 +1,171 @@
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.autoUpdateSkillIfStale = autoUpdateSkillIfStale;
37
+ exports.checkForCliUpdate = checkForCliUpdate;
38
+ const fs = __importStar(require("fs"));
39
+ const os = __importStar(require("os"));
40
+ const path = __importStar(require("path"));
41
+ const template_1 = require("./template");
42
+ const config_1 = require("../config");
43
+ /**
44
+ * Silent skill refresh. If the installed skill has a version marker that
45
+ * differs from the current CLI's bundled version, overwrite it.
46
+ *
47
+ * - No marker → user-customized skill; leave it alone.
48
+ * - Marker matches CLI version → up to date; no-op.
49
+ * - Marker differs → auto-upgrade. Print a one-line stderr notice.
50
+ *
51
+ * All errors are swallowed: this is a UX nicety, not a correctness gate.
52
+ * Skipped when `--json` is in argv so machine consumers see clean output.
53
+ */
54
+ function autoUpdateSkillIfStale() {
55
+ if (process.argv.includes('--json'))
56
+ return;
57
+ try {
58
+ const dest = (0, template_1.skillInstallPath)(os.homedir());
59
+ if (!fs.existsSync(dest))
60
+ return; // never installed; not our place
61
+ const installed = fs.readFileSync(dest, 'utf8');
62
+ const installedVersion = (0, template_1.readSkillVersion)(installed);
63
+ if (!installedVersion)
64
+ return; // user-customized; respect it
65
+ if (installedVersion === template_1.CLI_VERSION)
66
+ return; // already current
67
+ fs.writeFileSync(dest, template_1.SKILL_MD);
68
+ process.stderr.write(`sente: refreshed Claude Code skill (${installedVersion} -> ${template_1.CLI_VERSION}). Restart Claude Code to pick up changes.\n`);
69
+ }
70
+ catch {
71
+ // Best-effort. Never crash the user's command on update failures.
72
+ }
73
+ }
74
+ const CACHE_PATH = path.join(config_1.CREDENTIALS_DIR, 'update-check.json');
75
+ const CACHE_TTL_MS = 24 * 60 * 60 * 1000; // 24h
76
+ const REGISTRY_URL = 'https://registry.npmjs.org/@sente-labs%2fcli/latest';
77
+ /**
78
+ * Background-ish: hit the npm registry at most once per 24h to learn the
79
+ * latest published CLI version. If the local version is older, print a
80
+ * one-line nudge to stderr.
81
+ *
82
+ * - Network failures, registry errors → silent skip.
83
+ * - --json mode → silent skip.
84
+ * - No SENTE_DISABLE_UPDATE_CHECK env var path so users can opt out.
85
+ *
86
+ * Returns a promise the caller MAY await; the entry point fires-and-forgets
87
+ * by returning before the request settles, so the user's command doesn't
88
+ * pay the network latency.
89
+ */
90
+ async function checkForCliUpdate() {
91
+ if (process.argv.includes('--json'))
92
+ return;
93
+ if (process.env.SENTE_DISABLE_UPDATE_CHECK)
94
+ return;
95
+ try {
96
+ const cached = readCache();
97
+ const now = Date.now();
98
+ let latest = null;
99
+ if (cached && now - new Date(cached.checkedAt).getTime() < CACHE_TTL_MS) {
100
+ latest = cached.latestVersion;
101
+ }
102
+ else {
103
+ latest = await fetchLatestVersion();
104
+ if (latest)
105
+ writeCache({ checkedAt: new Date().toISOString(), latestVersion: latest });
106
+ }
107
+ if (!latest)
108
+ return;
109
+ if (isNewer(latest, template_1.CLI_VERSION)) {
110
+ process.stderr.write(`sente: ${template_1.CLI_VERSION} -> ${latest} available. Run: npm i -g @sente-labs/cli@latest\n`);
111
+ }
112
+ }
113
+ catch {
114
+ // ignore
115
+ }
116
+ }
117
+ async function fetchLatestVersion() {
118
+ try {
119
+ const controller = new AbortController();
120
+ const timeout = setTimeout(() => controller.abort(), 3000);
121
+ const res = await fetch(REGISTRY_URL, { signal: controller.signal });
122
+ clearTimeout(timeout);
123
+ if (!res.ok)
124
+ return null;
125
+ const body = (await res.json());
126
+ return typeof body.version === 'string' ? body.version : null;
127
+ }
128
+ catch {
129
+ return null;
130
+ }
131
+ }
132
+ function readCache() {
133
+ try {
134
+ if (!fs.existsSync(CACHE_PATH))
135
+ return null;
136
+ return JSON.parse(fs.readFileSync(CACHE_PATH, 'utf8'));
137
+ }
138
+ catch {
139
+ return null;
140
+ }
141
+ }
142
+ function writeCache(c) {
143
+ try {
144
+ if (!fs.existsSync(config_1.CREDENTIALS_DIR)) {
145
+ fs.mkdirSync(config_1.CREDENTIALS_DIR, { recursive: true, mode: 0o700 });
146
+ }
147
+ fs.writeFileSync(CACHE_PATH, JSON.stringify(c, null, 2));
148
+ }
149
+ catch {
150
+ // ignore
151
+ }
152
+ }
153
+ /**
154
+ * Compare two semver-ish version strings ("0.2.1", "0.3.0"). Returns true
155
+ * if `candidate` is strictly newer than `current`. Doesn't handle prerelease
156
+ * tags (we'll never ship those for now).
157
+ */
158
+ function isNewer(candidate, current) {
159
+ const a = candidate.split('.').map((n) => parseInt(n, 10));
160
+ const b = current.split('.').map((n) => parseInt(n, 10));
161
+ for (let i = 0; i < Math.max(a.length, b.length); i++) {
162
+ const ai = a[i] ?? 0;
163
+ const bi = b[i] ?? 0;
164
+ if (ai > bi)
165
+ return true;
166
+ if (ai < bi)
167
+ return false;
168
+ }
169
+ return false;
170
+ }
171
+ //# sourceMappingURL=autoupdate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"autoupdate.js","sourceRoot":"","sources":["../../src/skill/autoupdate.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiBA,wDAgBC;AAwBD,8CAsBC;AA/ED,uCAAyB;AACzB,uCAAyB;AACzB,2CAA6B;AAC7B,yCAAuF;AACvF,sCAA4C;AAE5C;;;;;;;;;;GAUG;AACH,SAAgB,sBAAsB;IACpC,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAAE,OAAO;IAC5C,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,IAAA,2BAAgB,EAAC,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;QAC5C,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,OAAO,CAAC,iCAAiC;QACnE,MAAM,SAAS,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAChD,MAAM,gBAAgB,GAAG,IAAA,2BAAgB,EAAC,SAAS,CAAC,CAAC;QACrD,IAAI,CAAC,gBAAgB;YAAE,OAAO,CAAC,8BAA8B;QAC7D,IAAI,gBAAgB,KAAK,sBAAW;YAAE,OAAO,CAAC,kBAAkB;QAChE,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,mBAAQ,CAAC,CAAC;QACjC,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,uCAAuC,gBAAgB,OAAO,sBAAW,8CAA8C,CACxH,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,kEAAkE;IACpE,CAAC;AACH,CAAC;AAOD,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,wBAAe,EAAE,mBAAmB,CAAC,CAAC;AACnE,MAAM,YAAY,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,MAAM;AAChD,MAAM,YAAY,GAAG,qDAAqD,CAAC;AAE3E;;;;;;;;;;;;GAYG;AACI,KAAK,UAAU,iBAAiB;IACrC,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAAE,OAAO;IAC5C,IAAI,OAAO,CAAC,GAAG,CAAC,0BAA0B;QAAE,OAAO;IACnD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;QAC3B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,IAAI,MAAM,GAAkB,IAAI,CAAC;QACjC,IAAI,MAAM,IAAI,GAAG,GAAG,IAAI,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,GAAG,YAAY,EAAE,CAAC;YACxE,MAAM,GAAG,MAAM,CAAC,aAAa,CAAC;QAChC,CAAC;aAAM,CAAC;YACN,MAAM,GAAG,MAAM,kBAAkB,EAAE,CAAC;YACpC,IAAI,MAAM;gBAAE,UAAU,CAAC,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,aAAa,EAAE,MAAM,EAAE,CAAC,CAAC;QACzF,CAAC;QACD,IAAI,CAAC,MAAM;YAAE,OAAO;QACpB,IAAI,OAAO,CAAC,MAAM,EAAE,sBAAW,CAAC,EAAE,CAAC;YACjC,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,UAAU,sBAAW,OAAO,MAAM,oDAAoD,CACvF,CAAC;QACJ,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,SAAS;IACX,CAAC;AACH,CAAC;AAED,KAAK,UAAU,kBAAkB;IAC/B,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACzC,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,IAAI,CAAC,CAAC;QAC3D,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,YAAY,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC;QACrE,YAAY,CAAC,OAAO,CAAC,CAAC;QACtB,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,OAAO,IAAI,CAAC;QACzB,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAyB,CAAC;QACxD,OAAO,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;IAChE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,SAAS;IAChB,IAAI,CAAC;QACH,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC;YAAE,OAAO,IAAI,CAAC;QAC5C,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAgB,CAAC;IACxE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,UAAU,CAAC,CAAc;IAChC,IAAI,CAAC;QACH,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,wBAAe,CAAC,EAAE,CAAC;YACpC,EAAE,CAAC,SAAS,CAAC,wBAAe,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAClE,CAAC;QACD,EAAE,CAAC,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAC3D,CAAC;IAAC,MAAM,CAAC;QACP,SAAS;IACX,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,SAAS,OAAO,CAAC,SAAiB,EAAE,OAAe;IACjD,MAAM,CAAC,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;IAC3D,MAAM,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;IACzD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QACtD,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACrB,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACrB,IAAI,EAAE,GAAG,EAAE;YAAE,OAAO,IAAI,CAAC;QACzB,IAAI,EAAE,GAAG,EAAE;YAAE,OAAO,KAAK,CAAC;IAC5B,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -33,22 +33,36 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.SKILL_MD = void 0;
36
+ exports.SKILL_MD = exports.CLI_VERSION = void 0;
37
37
  exports.skillInstallPath = skillInstallPath;
38
+ exports.readSkillVersion = readSkillVersion;
38
39
  const fs = __importStar(require("fs"));
39
40
  const path = __importStar(require("path"));
41
+ // Read package.json once at module load so the bundled skill carries the
42
+ // running CLI version. The file is two levels up from dist/skill/template.js
43
+ // at runtime and from src/skill/template.ts at dev time -- same depth.
44
+ const PKG = JSON.parse(fs.readFileSync(path.join(__dirname, '..', '..', 'package.json'), 'utf8'));
45
+ exports.CLI_VERSION = PKG.version;
46
+ const RAW_SKILL = fs.readFileSync(path.join(__dirname, 'SKILL.md'), 'utf8');
40
47
  /**
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.
48
+ * Bundled SKILL.md content with `__CLI_VERSION__` replaced by the actual
49
+ * CLI version. The marker is embedded in an HTML comment so we can detect
50
+ * drift at runtime without parsing the YAML frontmatter.
50
51
  */
52
+ exports.SKILL_MD = RAW_SKILL.replace('__CLI_VERSION__', exports.CLI_VERSION);
53
+ /** Standard Claude Code skill path. */
51
54
  function skillInstallPath(homeDir) {
52
55
  return path.join(homeDir, '.claude', 'skills', 'sente', 'SKILL.md');
53
56
  }
57
+ const VERSION_MARKER_RE = /<!--\s*sente-cli-version:\s*([0-9A-Za-z.\-_]+)/;
58
+ /**
59
+ * Read the embedded version marker from a skill file's contents.
60
+ * Returns null when:
61
+ * - The file has no marker (user-customized; we leave it alone).
62
+ * - The marker is malformed.
63
+ */
64
+ function readSkillVersion(content) {
65
+ const m = content.match(VERSION_MARKER_RE);
66
+ return m?.[1] ?? null;
67
+ }
54
68
  //# sourceMappingURL=template.js.map
@@ -1 +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"}
1
+ {"version":3,"file":"template.js","sourceRoot":"","sources":["../../src/skill/template.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsBA,4CAEC;AAUD,4CAGC;AArCD,uCAAyB;AACzB,2CAA6B;AAE7B,yEAAyE;AACzE,6EAA6E;AAC7E,uEAAuE;AACvE,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CACpB,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAC1E,CAAC;AAEW,QAAA,WAAW,GAAW,GAAG,CAAC,OAAO,CAAC;AAE/C,MAAM,SAAS,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC,EAAE,MAAM,CAAC,CAAC;AAE5E;;;;GAIG;AACU,QAAA,QAAQ,GAAW,SAAS,CAAC,OAAO,CAAC,iBAAiB,EAAE,mBAAW,CAAC,CAAC;AAElF,uCAAuC;AACvC,SAAgB,gBAAgB,CAAC,OAAe;IAC9C,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC;AACtE,CAAC;AAED,MAAM,iBAAiB,GAAG,gDAAgD,CAAC;AAE3E;;;;;GAKG;AACH,SAAgB,gBAAgB,CAAC,OAAe;IAC9C,MAAM,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;IAC3C,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;AACxB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sente-labs/cli",
3
- "version": "0.2.1",
3
+ "version": "0.4.0",
4
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
5
  "bin": {
6
6
  "sente": "dist/index.js"