diff --git a/CHANGELOG.md b/CHANGELOG.md
index c03b301..6de68d2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 1.18.0
+
+Adds `activitysmith push dismiss` to clear a Push Notification you already sent from the Lock Screen and Notification Center, using the Notification ID from its send output. History keeps it. Also adds `activitysmith metrics create` to create a Lock Screen Widget metric and set its label and format, such as currency or percent.
+
## 1.17.0
Adds approvals. Your agents can now ask for your approval before they act, such as sending an email or deploying. `activitysmith approval request` shows the request on your iPhone and waits for your answer. It exits with code 0 only when you approve, so the agent continues only after that. `approval wait`, `approval get`, and `approval cancel` work with a request you already sent.
diff --git a/package.json b/package.json
index 2c115b8..21d53f2 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "activitysmith-cli",
- "version": "1.17.0",
+ "version": "1.18.0",
"description": "Command-line interface for ActivitySmith. Send Push Notifications, start, update, and end Live Activities, and set App Icon Badge Counts from your terminal.",
"keywords": [
"activitysmith",
@@ -42,7 +42,7 @@
"test": "node --test"
},
"dependencies": {
- "activitysmith": "^1.14.0",
+ "activitysmith": "^1.15.0",
"commander": "^12.1.0"
}
}
diff --git a/skills/activitysmith/SKILL.md b/skills/activitysmith/SKILL.md
index 820b24a..8bba4d2 100644
--- a/skills/activitysmith/SKILL.md
+++ b/skills/activitysmith/SKILL.md
@@ -47,6 +47,7 @@ If it prints `No paired devices.`, give the user the App Store link it prints. A
- Push Notification: one important event, completion, blocker, review request, or handoff.
- Push Notification replacement: an earlier Push Notification is out of date and should be swapped, not stacked.
+- Push Notification dismissal: an earlier Push Notification no longer needs attention, such as a question the user already answered somewhere else.
- Push Notification with redirection: tapping the notification should open a URL or run a specific Shortcut that already exists on the user's iPhone.
- Push Notification with actions: long-press should show up to 4 buttons.
- Live Activity: a real-time metric, operational state, or ongoing task should stay visible.
@@ -95,6 +96,14 @@ Use this to update a Push Notification you already sent, for example when an inc
-m "Error rate is back to normal. Incident resolved."
```
+### Dismiss A Notification That No Longer Needs Attention
+
+Use this when a Push Notification you sent is no longer relevant, for example after you asked the user a question and they answered it in the terminal. Pass its `Notification ID` to clear it from the Lock Screen and Notification Center. History keeps it.
+
+```bash
+activitysmith push dismiss "Xk3pQ8mV2nR7tL9wB4yZ"
+```
+
### Notify With A Shortcut Tap Target
Use this when tapping the notification should run a specific Shortcut that already exists on the user's iPhone.
@@ -223,6 +232,12 @@ String values work too:
activitysmith metrics update prod.status healthy
```
+The first value creates the metric. To set its label and format, such as currency or percent, run `metrics create` first. It is safe to run again:
+
+```bash
+activitysmith metrics create mrr --label MRR --format currency --currency-code USD
+```
+
### Store An API Key For CI
Use this when code that runs elsewhere, like a CI job, server, or cron job, should send Push Notifications or Live Activities. Pipe the key straight into the secret store so it never appears in chat or terminal output:
diff --git a/src/cli.js b/src/cli.js
index 7cae7d0..a01221e 100755
--- a/src/cli.js
+++ b/src/cli.js
@@ -1282,10 +1282,11 @@ const outputResult = (payload, options, lines) => {
lines.filter(Boolean).forEach((line) => console.log(line));
};
-program
+const pushCommand = program
.command("push")
- .description("Send a push notification, or replace one you already sent")
- .requiredOption("--title
", "Push title")
+ .description("Send a push notification, or replace or dismiss one you already sent")
+ // Not requiredOption: commander would then demand --title for the dismiss subcommand too.
+ .option("--title ", "Push title (required to send or replace)")
.option(
"--replace ",
"Silently replace a sent push notification, using the notification ID from its send output"
@@ -1322,6 +1323,10 @@ program
.action(async (options) => {
const globalOptions = program.opts();
+ if (options.title === undefined) {
+ pushCommand.error("error: required option '--title ' not specified");
+ }
+
try {
const apiKey = await requireApiKey(globalOptions);
const client = createClient(apiKey);
@@ -1390,6 +1395,35 @@ program
}
});
+pushCommand
+ .command("dismiss")
+ .description(
+ "Clear a sent push notification from the Lock Screen and Notification Center. History keeps it."
+ )
+ .argument("", "Notification ID from the push send output")
+ .action(async (notificationId) => {
+ const globalOptions = program.opts();
+
+ try {
+ const id = String(notificationId).trim();
+ if (!id) {
+ throw new Error("Notification ID is required.");
+ }
+ const apiKey = await requireApiKey(globalOptions);
+ const client = createClient(apiKey);
+ const response = await client.notifications.dismiss(id);
+ const dismissedAt = response?.dismissed_at ?? response?.dismissedAt;
+
+ outputResult(response, globalOptions, [
+ "Push dismissed.",
+ `Notification ID: ${response?.notification_id ?? response?.notificationId ?? id}`,
+ dismissedAt ? `Dismissed at: ${dismissedAt}` : null,
+ ]);
+ } catch (error) {
+ await handleError(error, globalOptions);
+ }
+ });
+
program
.command("badge")
.alias("badge-count")
@@ -1432,7 +1466,42 @@ program
const metricsCommand = program
.command("metrics")
.alias("metric")
- .description("Update widget metric values");
+ .description("Create widget metrics and update their values");
+
+metricsCommand
+ .command("create")
+ .description("Create a widget metric, or update the fields you pass for an existing one")
+ .argument("", "Metric key")
+ .option("--label