Skip to content

Commit f8676df

Browse files
committed
feat: add status and delete capabilities to X platform
1 parent 7c4e694 commit f8676df

8 files changed

Lines changed: 283 additions & 7 deletions

File tree

skills/autocli/references/providers/x.md

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ Interact with X/Twitter using an imported browser session and browser-backed wri
2626

2727
- `autocli social x login`
2828
- `autocli social x login --cookies ./x.cookies.json`
29-
- `autocli social x post "Launching AutoCLI"`
29+
- `autocli social x status`
3030
- `autocli social x capabilities --json`
3131

3232
## Default Command
@@ -59,6 +59,19 @@ Options:
5959
- `--browser`: Open a real browser, wait for manual login, then save the extracted session (default when no cookie flags are provided)
6060
- `--browser-timeout <seconds>`: Maximum seconds to wait for manual browser login (default: 600)
6161

62+
### `status`
63+
64+
Usage:
65+
```bash
66+
autocli social x status [options]
67+
```
68+
69+
Show the saved X session status
70+
71+
Options:
72+
73+
- `--account <name>`: Optional override for a specific saved X session
74+
6275
### `post`
6376

6477
Usage:
@@ -135,6 +148,23 @@ Options:
135148
- `--limit <number>`: Maximum number of posts to return (1-25, default: 5)
136149
- `--account <name>`: Optional override for a specific saved X session
137150

151+
### `delete`
152+
153+
Usage:
154+
```bash
155+
autocli social x delete [options] <target>
156+
```
157+
158+
Aliases: `remove`
159+
160+
Delete your own X post by URL or tweet ID through a browser-backed action flow
161+
162+
Options:
163+
164+
- `--account <name>`: Optional override for a specific saved X session
165+
- `--browser`: Force the delete through the shared AutoCLI browser profile instead of the invisible browser-backed path
166+
- `--browser-timeout <seconds>`: Maximum seconds to allow the browser action to complete
167+
138168
### `like`
139169

140170
Usage:

src/platforms/social/x/__tests__/write-capabilities.test.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,21 @@ import { buildPlatformCommand } from "../../../../core/runtime/build-platform-co
55
import { xPlatformDefinition } from "../manifest.js";
66

77
describe("x write capability command surface", () => {
8+
test("exposes a status command for saved-session health checks", () => {
9+
const command = buildPlatformCommand(xPlatformDefinition);
10+
const byName = new Map(command.commands.map((entry) => [entry.name(), entry]));
11+
const subcommand = byName.get("status");
12+
13+
expect(subcommand).toBeDefined();
14+
expect(subcommand!.options.map((option) => option.flags)).toContain("--account <name>");
15+
});
16+
817
test("keeps browser-backed flags on all write commands", () => {
918
const command = buildPlatformCommand(xPlatformDefinition);
1019

1120
const byName = new Map(command.commands.map((entry) => [entry.name(), entry]));
1221

13-
for (const name of ["post", "comment", "like", "unlike"] as const) {
22+
for (const name of ["post", "comment", "delete", "like", "unlike"] as const) {
1423
const subcommand = byName.get(name);
1524
expect(subcommand).toBeDefined();
1625

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import type { Command } from "commander";
2+
3+
import { Logger } from "../../../../logger.js";
4+
import { printActionResult, resolveCommandContext, runCommandAction } from "../../../../utils/cli.js";
5+
import { parseBrowserTimeoutSeconds } from "../../../shared/cookie-login.js";
6+
7+
import { xAdapter } from "../adapter.js";
8+
9+
import type { PlatformCapability } from "../../../../core/runtime/platform-definition.js";
10+
11+
export const xDeleteCapability: PlatformCapability = {
12+
id: "delete",
13+
register(command: Command) {
14+
command
15+
.command("delete <target>")
16+
.alias("remove")
17+
.description("Delete your own X post by URL or tweet ID through a browser-backed action flow")
18+
.option("--account <name>", "Optional override for a specific saved X session")
19+
.option("--browser", "Force the delete through the shared AutoCLI browser profile instead of the invisible browser-backed path")
20+
.option("--browser-timeout <seconds>", "Maximum seconds to allow the browser action to complete", parseBrowserTimeoutSeconds)
21+
.action(async (target, options, cmd) => {
22+
const ctx = resolveCommandContext(cmd);
23+
const logger = new Logger(ctx);
24+
const spinner = logger.spinner("Deleting X post...");
25+
await runCommandAction({
26+
spinner,
27+
successMessage: "X post deleted.",
28+
action: () =>
29+
xAdapter.deleteTweet({
30+
account: options.account,
31+
target,
32+
browser: Boolean(options.browser),
33+
browserTimeoutSeconds: options.browserTimeout as number | undefined,
34+
}),
35+
onSuccess: (result) => {
36+
printActionResult(result, ctx.json);
37+
},
38+
});
39+
});
40+
},
41+
};

src/platforms/social/x/capabilities/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import { xCommentCapability } from "./comment.js";
2+
import { xDeleteCapability } from "./delete.js";
23
import { xLikeCapability } from "./like.js";
34
import { xLoginCapability } from "./login.js";
45
import { xPostCapability } from "./post.js";
56
import { xProfileIdCapability } from "./profileid.js";
67
import { xSearchCapability } from "./search.js";
8+
import { xStatusCapability } from "./status.js";
79
import { xTweetIdCapability } from "./tweetid.js";
810
import { xTweetsCapability } from "./tweets.js";
911
import { xUnlikeCapability } from "./unlike.js";
@@ -12,11 +14,13 @@ import type { PlatformCapability } from "../../../../core/runtime/platform-defin
1214

1315
export const xCapabilities: readonly PlatformCapability[] = [
1416
xLoginCapability,
17+
xStatusCapability,
1518
xPostCapability,
1619
xSearchCapability,
1720
xTweetIdCapability,
1821
xProfileIdCapability,
1922
xTweetsCapability,
23+
xDeleteCapability,
2024
xLikeCapability,
2125
xUnlikeCapability,
2226
xCommentCapability,
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { createAdapterActionCapability } from "../../../../core/runtime/capability-helpers.js";
2+
3+
import { xAdapter } from "../adapter.js";
4+
import { printXStatusResult } from "../output.js";
5+
6+
export const xStatusCapability = createAdapterActionCapability({
7+
id: "status",
8+
command: "status",
9+
description: "Show the saved X session status",
10+
spinnerText: "Checking X session...",
11+
successMessage: "X session checked.",
12+
options: [{ flags: "--account <name>", description: "Optional override for a specific saved X session" }],
13+
action: ({ options }) => xAdapter.statusAction(options.account as string | undefined),
14+
onSuccess: printXStatusResult,
15+
});

src/platforms/social/x/manifest.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,14 @@ export const xPlatformDefinition: PlatformDefinition = {
1515
examples: [
1616
"autocli social x login",
1717
"autocli social x login --cookies ./x.cookies.json",
18+
"autocli social x status",
1819
'autocli social x post "Launching AutoCLI"',
1920
'autocli social x post "Launching AutoCLI" --browser',
2021
'autocli social x search "openai" --limit 5',
2122
"autocli social x tweetid https://x.com/user/status/1234567890",
2223
"autocli social x profileid @OpenAI",
2324
"autocli social x tweets @OpenAI --limit 5",
25+
"autocli social x delete https://x.com/user/status/1234567890",
2426
"autocli social x like https://x.com/user/status/1234567890",
2527
"autocli social x unlike 1234567890",
2628
],

src/platforms/social/x/output.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,3 +147,32 @@ export function printXProfileResult(result: AdapterActionResult, json: boolean):
147147
console.log(preview);
148148
}
149149
}
150+
151+
export function printXStatusResult(result: AdapterActionResult, json: boolean): void {
152+
if (json) {
153+
printJson(result);
154+
return;
155+
}
156+
157+
printActionResult(result, false);
158+
const data = result.data;
159+
if (!data || typeof data !== "object") {
160+
return;
161+
}
162+
163+
if (typeof data.status === "string") {
164+
console.log(`status: ${data.status}`);
165+
}
166+
167+
if (typeof data.connected === "boolean") {
168+
console.log(`connected: ${data.connected ? "yes" : "no"}`);
169+
}
170+
171+
if (typeof data.lastValidatedAt === "string") {
172+
console.log(`lastValidatedAt: ${data.lastValidatedAt}`);
173+
}
174+
175+
if (typeof data.details === "string" && data.details.length > 0) {
176+
console.log(`details: ${data.details}`);
177+
}
178+
}

0 commit comments

Comments
 (0)