Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
},
"metadata": {
"description": "TryPost plugin for planning, scheduling and publishing social media posts",
"version": "2.0.1"
"version": "2.0.3"
},
"plugins": [
{
Expand Down
2 changes: 1 addition & 1 deletion .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "trypost",
"version": "2.0.1",
"version": "2.0.3",
"description": "Plan, schedule and publish social media posts, manage your queue and approvals, and read post analytics in TryPost.",
"author": {
"name": "Paulo Castellano",
Expand Down
3 changes: 2 additions & 1 deletion .codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "trypost",
"version": "2.0.1",
"version": "2.0.3",
"description": "Plan, schedule and publish social media posts, manage your queue and approvals, and read post analytics in TryPost.",
"author": {
"name": "TryPost",
Expand Down Expand Up @@ -91,6 +91,7 @@
}
]
},
"demo_recording_url": "https://drive.google.com/file/d/1vq3Z9HHRRo2SBt_z2UxeGpN9HMbHPPyG/view?usp=sharing",
"commerce": false,
"commerce_description": "TryPost does not sell products or process payments in ChatGPT."
},
Expand Down
2 changes: 1 addition & 1 deletion .cursor-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
{
"name": "trypost",
"source": "./",
"version": "2.0.1",
"version": "2.0.3",
"description": "Plan, schedule and publish social media posts, manage your queue and approvals, and read post analytics in TryPost.",
"author": {
"name": "Paulo Castellano",
Expand Down
2 changes: 1 addition & 1 deletion .cursor-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "trypost",
"version": "2.0.1",
"version": "2.0.3",
"description": "Plan, schedule and publish social media posts, manage your queue and approvals, and read post analytics in TryPost.",
"author": {
"name": "Paulo Castellano",
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,9 @@ jobs:
run: node scripts/build.ts

- name: List the ZIP
run: unzip -l build/trypost-chatgpt.zip
run: unzip -l build/trypost-chatgpt-*.zip

- uses: actions/upload-artifact@v7
with:
name: trypost-chatgpt
path: build/trypost-chatgpt.zip
path: build/trypost-chatgpt-*.zip
2 changes: 1 addition & 1 deletion .grok-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "trypost",
"version": "2.0.1",
"version": "2.0.3",
"description": "Plan, schedule and publish social media posts, manage your queue and approvals, and read post analytics in TryPost.",
"author": {
"name": "Paulo Castellano",
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ This repository packages TryPost as a plugin for Claude Code, Cursor, Grok, Chat
- Review needs exactly five positive and three negative test cases. Positive cases name tools the skill documents.
- Never mention prices, plans, trials, discounts or upgrades in the listing or the skill.
- Never put reviewer credentials, the demo video, client secrets or the domain verification token in the package; they go in the OpenAI dashboard. The verification token is served by the TryPost app at `https://app.trypost.it/.well-known/openai-apps-challenge`.
- Build the upload with `node scripts/build.ts`: it writes `build/trypost-chatgpt.zip` (git-ignored) from `HEAD`. The list of packed paths lives only in `scripts/build.ts` (`pluginPaths`); `tests/build.test.ts` checks it covers everything the manifest references. Never commit the ZIP.
- Build the upload with `node scripts/build.ts`: it writes `build/trypost-chatgpt-<YYYYMMDDHHMMSS>.zip` (git-ignored, named by build time) from `HEAD`. Other targets follow the same `build/trypost-<target>-<YYYYMMDDHHMMSS>.zip` pattern. The list of packed paths lives only in `scripts/build.ts` (`pluginPaths`); `tests/build.test.ts` checks it covers everything the manifest references. Never commit the ZIP.

## Skill (`SKILL.md`)

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ Build the ZIP to upload at [platform.openai.com/plugins](https://platform.openai
node scripts/build.ts
```

It writes `build/trypost-chatgpt.zip` (git-ignored) from the last commit, so commit your changes before building. The demo video URL goes in `.codex-plugin/plugin.json` (`extensions.com.openai.review.demo_recording_url`); reviewer credentials and the domain verification token are entered in the dashboard, never in the package.
It writes `build/trypost-chatgpt-<YYYYMMDDHHMMSS>.zip` (git-ignored, named by build time so builds sort by date) from the last commit, so commit your changes before building. The demo video URL goes in `.codex-plugin/plugin.json` (`extensions.com.openai.review.demo_recording_url`); reviewer credentials and the domain verification token are entered in the dashboard, never in the package.

### Cursor plugin

Expand Down
4 changes: 2 additions & 2 deletions SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ App: https://app.trypost.it

## Connecting

- Every tool here comes from the `trypost` MCP server and is written `trypost:<tool>`. Clients may show the same tool in another form, for example `trypost.list-social-accounts-tool` or `mcp__trypost__list_social_accounts_tool`. If no TryPost tool is listed, search the client's tools for `trypost` before concluding they are missing: some clients load MCP tools on demand.
- The tools are listed only once the user has connected TryPost. If they are missing, or a call asks the user to sign in, the user needs to connect TryPost from the client's plugin or connector settings: that opens TryPost's sign-in (https://app.trypost.it/login), where they pick the workspace to use. No account yet: https://app.trypost.it/register.
- Every tool here comes from the `trypost` MCP server and is written `trypost:<tool>`. Clients may load MCP tools on demand and show them in another form, for example `trypost.list-social-accounts-tool` or `mcp__trypost__list_social_accounts_tool`. Before saying a TryPost tool is unavailable, search the client's tools for `trypost`.
- Only when that search finds no TryPost tool, or a call asks the user to sign in, is TryPost not connected: the user connects it from the client's plugin or connector settings, which opens TryPost's sign-in, where they pick the workspace to use. Never send them to the TryPost website to sign in: that does not connect the plugin. No account yet: https://app.trypost.it/register.
- The TryPost web app in a browser is not a substitute for these tools.

## Authentication
Expand Down
15 changes: 11 additions & 4 deletions scripts/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,23 @@ export const root = join(import.meta.dirname, '..');

export const pluginPaths = ['.codex-plugin', '.mcp.json', 'skills', 'assets'];

export const outputFile = 'build/trypost-chatgpt.zip';
const pad = (value: number): string => String(value).padStart(2, '0');

export const timestamp = (date: Date): string =>
`${date.getFullYear()}${pad(date.getMonth() + 1)}${pad(date.getDate())}${pad(date.getHours())}${pad(date.getMinutes())}${pad(date.getSeconds())}`;

export const outputFile = (date: Date = new Date()): string => `build/trypost-chatgpt-${timestamp(date)}.zip`;

export const buildPluginZip = (date: Date = new Date()): string => {
const file = outputFile(date);

export const buildPluginZip = (): string => {
mkdirSync(join(root, 'build'), { recursive: true });

execFileSync('git', ['archive', '--format=zip', '--prefix=trypost/', '-o', outputFile, 'HEAD', ...pluginPaths], {
execFileSync('git', ['archive', '--format=zip', '--prefix=trypost/', '-o', file, 'HEAD', ...pluginPaths], {
cwd: root,
});

return join(root, outputFile);
return join(root, file);
};

if (import.meta.main) {
Expand Down
9 changes: 5 additions & 4 deletions skills/setup/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ description: Checks that TryPost is connected after the plugin is installed and

# TryPost setup

1. Call `trypost:get-workspace-tool`. When it answers, tell the user which workspace is connected and whether their posts need approval (`me.requires_approval`).
2. Call `trypost:list-social-accounts-tool` and list the connected channels. A channel with `status` `token_expired` or `disconnected` must be reconnected in the TryPost app (Accounts). With no channel, the user connects one in the TryPost app at https://app.trypost.it: channels cannot be connected through the plugin.
3. If the TryPost tools are not listed, or a call asks the user to sign in, they have not connected TryPost yet. Ask them to connect TryPost from the plugin settings: it opens TryPost's sign-in (https://app.trypost.it/login), where they pick the workspace this connection may use. No account yet: https://app.trypost.it/register. Some clients show the tools as `trypost.<tool>` or `mcp__trypost__<tool_name>`; search for `trypost` before concluding they are missing.
4. To use another workspace, disconnect TryPost in the plugin settings and connect again, picking that workspace.
1. Find the TryPost tools first. Clients may load MCP tools on demand and name them differently, for example `trypost.get-workspace-tool` or `mcp__trypost__get_workspace_tool`. Search the client's tools for `trypost` before anything else. Only when the search finds none, go to step 4.
2. Call `trypost:get-workspace-tool`. When it answers, tell the user which workspace is connected and whether their posts need approval (`me.requires_approval`). If it asks the user to sign in, go to step 4.
3. Call `trypost:list-social-accounts-tool` and list the connected channels. A channel with `status` `token_expired` or `disconnected` must be reconnected in the TryPost app (Accounts). With no channel, the user connects one in the TryPost app at https://app.trypost.it: channels cannot be connected through the plugin.
4. Only when step 1 found no TryPost tool, or a call asked the user to sign in: TryPost is not connected yet. Ask the user to connect TryPost from the plugin settings: Connect opens TryPost's sign-in, where they pick the workspace this connection may use. Never send them to the TryPost website to sign in: that does not connect the plugin. No account yet: https://app.trypost.it/register.
5. To use another workspace, disconnect TryPost in the plugin settings and connect again, picking that workspace.
4 changes: 2 additions & 2 deletions skills/trypost/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ App: https://app.trypost.it

## Connecting

- Every tool here comes from the `trypost` MCP server and is written `trypost:<tool>`. Clients may show the same tool in another form, for example `trypost.list-social-accounts-tool` or `mcp__trypost__list_social_accounts_tool`. If no TryPost tool is listed, search the client's tools for `trypost` before concluding they are missing: some clients load MCP tools on demand.
- The tools are listed only once the user has connected TryPost. If they are missing, or a call asks the user to sign in, the user needs to connect TryPost from the client's plugin or connector settings: that opens TryPost's sign-in (https://app.trypost.it/login), where they pick the workspace to use. No account yet: https://app.trypost.it/register.
- Every tool here comes from the `trypost` MCP server and is written `trypost:<tool>`. Clients may load MCP tools on demand and show them in another form, for example `trypost.list-social-accounts-tool` or `mcp__trypost__list_social_accounts_tool`. Before saying a TryPost tool is unavailable, search the client's tools for `trypost`.
- Only when that search finds no TryPost tool, or a call asks the user to sign in, is TryPost not connected: the user connects it from the client's plugin or connector settings, which opens TryPost's sign-in, where they pick the workspace to use. Never send them to the TryPost website to sign in: that does not connect the plugin. No account yet: https://app.trypost.it/register.
- The TryPost web app in a browser is not a substitute for these tools.

## Authentication
Expand Down
19 changes: 15 additions & 4 deletions tests/build.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import assert from 'node:assert/strict';
import { execFileSync } from 'node:child_process';
import { rmSync } from 'node:fs';
import { describe, test } from 'node:test';

import { buildPluginZip, outputFile, pluginPaths, root } from '../scripts/build.ts';
import { buildPluginZip, outputFile, pluginPaths, root, timestamp } from '../scripts/build.ts';
import { exists, read, readJson } from './helpers.ts';
import type { PluginManifest } from './types.ts';

Expand All @@ -28,11 +29,21 @@ describe('OpenAI plugin build', () => {
}
});

test('names the ZIP trypost-chatgpt with the build time, so builds sort by date', () => {
const date = new Date(2026, 9, 5, 7, 8, 9);

assert.equal(timestamp(date), '20261005070809');
assert.equal(outputFile(date), 'build/trypost-chatgpt-20261005070809.zip');
});

test('writes a ZIP into build/ with the plugin under one trypost/ folder', () => {
const file = buildPluginZip();
const date = new Date();
const file = buildPluginZip(date);
const entries = zipEntries(file);

assert.equal(file, `${root}/${outputFile}`);
rmSync(file);

assert.equal(file, `${root}/${outputFile(date)}`);

for (const entry of ['.codex-plugin/plugin.json', '.mcp.json', 'skills/trypost/SKILL.md', 'skills/setup/SKILL.md', 'assets/icon-512.png']) {
assert.ok(entries.includes(`trypost/${entry}`), `trypost/${entry} is missing from the ZIP`);
Expand All @@ -49,7 +60,7 @@ describe('OpenAI plugin build', () => {
});

test('keeps the build output out of git', () => {
assert.doesNotThrow(() => execFileSync('git', ['check-ignore', '--quiet', outputFile], { cwd: root }));
assert.doesNotThrow(() => execFileSync('git', ['check-ignore', '--quiet', outputFile()], { cwd: root }));
});

test('the README builds the ZIP with the script', () => {
Expand Down
4 changes: 4 additions & 0 deletions tests/openai.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ describe('OpenAI review', () => {
}
});

test('links a public https demo recording', () => {
assert.match(openai.review?.demo_recording_url ?? '', /^https:\/\/\S+$/);
});

test('declares no commerce and keeps reviewer access out of the package', () => {
assert.equal(openai.review?.commerce, false);
assert.doesNotMatch(JSON.stringify(manifest), /test_credentials|reviewer_instructions|password/i);
Expand Down
18 changes: 15 additions & 3 deletions tests/skill.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,16 @@ describe('skill tool names and sign-in', () => {
assert.deepEqual(unqualifiedToolNames(skill), []);
});

test('asks to search for the tools before asking the user to sign in', () => {
assert.ok(skill.indexOf("search the client's tools for `trypost`") > -1);
assert.ok(skill.indexOf("search the client's tools for `trypost`") < skill.indexOf("plugin or connector settings"));
});

test('explains where the tools come from and how the user signs in', () => {
assert.ok(skill.includes('## Connecting'));
assert.ok(skill.includes('mcp__trypost__list_social_accounts_tool'));
assert.ok(skill.includes('https://app.trypost.it/login'));
assert.ok(skill.includes('plugin or connector settings'));
assert.ok(!skill.includes('https://app.trypost.it/login'));
assert.ok(skill.includes('https://app.trypost.it/register'));
});
});
Expand Down Expand Up @@ -97,8 +103,14 @@ describe('setup skill', () => {
}
});

test('tells the user how to sign in and where to create an account', () => {
assert.ok(setup.includes('https://app.trypost.it/login'));
test('finds the tools first and asks to sign in only when they are missing', () => {
assert.match(setup, /^1\. Find the TryPost tools first\./m);
assert.ok(setup.indexOf("Search the client's tools for `trypost`") < setup.indexOf('connect TryPost from the plugin settings'));
});

test('signs the user in through the plugin settings, never the website', () => {
assert.ok(setup.includes('connect TryPost from the plugin settings'));
assert.ok(!setup.includes('https://app.trypost.it/login'));
assert.ok(setup.includes('https://app.trypost.it/register'));
});
});
1 change: 1 addition & 0 deletions tests/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export type OpenAiSettings = {
hooks?: unknown;
apps?: unknown;
review?: {
demo_recording_url?: string;
test_cases?: {
positive?: ReviewTestCase[];
negative?: ReviewTestCase[];
Expand Down
Loading