shiply-cli 0.3.0 → 0.4.1

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/confetti.js CHANGED
@@ -2,7 +2,7 @@ const GLYPHS = ['✦', '✧', '★', '☆', '●', '◆', '▲', '■', '♥', '
2
2
  const COLORS = [196, 202, 208, 220, 226, 118, 46, 51, 45, 39, 99, 201, 207, 213];
3
3
  const ESC = '\x1b';
4
4
  /** Full-width ANSI confetti burst — sites going live deserve a party. */
5
- export function confetti(message = '🎉 SITE IS LIVE 🎉') {
5
+ export function confetti(message = ' SITE IS LIVE ') {
6
6
  const cols = Math.min(process.stdout.columns ?? 80, 100);
7
7
  const rows = Math.min(Math.max((process.stdout.rows ?? 24) - 6, 8), 18);
8
8
  let out = '\n';
package/dist/index.js CHANGED
@@ -4,6 +4,7 @@ import { parseArgs } from 'node:util';
4
4
  import { confetti } from './confetti.js';
5
5
  import { loadApiKey, saveApiKey } from './config.js';
6
6
  import { api, DEFAULT_BASE, publish, resolveBase } from './publish.js';
7
+ import { installSkill } from './skill.js';
7
8
  import { readState, writeState } from './state.js';
8
9
  import { checkReadiness, targetToHostname } from './status.js';
9
10
  const HELP = `shiply — instant static hosting for agents (https://shiply.now)
@@ -13,6 +14,8 @@ Usage:
13
14
  Re-running UPDATES the same site (state in .shiply.json)
14
15
  shiply update <dir> Same as publish when .shiply.json exists
15
16
  shiply status <slug-or-domain> [--wait] SSL + readiness check (agent-friendly output)
17
+ shiply skill [--project] Install the shiply skill for your AI agent
18
+ (global ~/.claude/skills, or ./.claude/skills with --project)
16
19
  shiply login [--email <address>] Email a 6-digit code, mint + save an API key
17
20
  shiply help
18
21
 
@@ -69,6 +72,7 @@ async function main() {
69
72
  email: { type: 'string' },
70
73
  wait: { type: 'boolean' },
71
74
  'new-site': { type: 'boolean' },
75
+ project: { type: 'boolean' },
72
76
  timeout: { type: 'string' },
73
77
  'no-confetti': { type: 'boolean' },
74
78
  help: { type: 'boolean', short: 'h' },
@@ -121,7 +125,7 @@ async function main() {
121
125
  if (live.status < 400) {
122
126
  console.log(`SITE_READY url=${res.siteUrl} status=${live.status}`);
123
127
  if (!values['no-confetti'])
124
- console.log(confetti(`🎉 ${host} IS LIVE 🎉`));
128
+ console.log(confetti(`⛵ ${host} IS LIVE ⛵`));
125
129
  }
126
130
  }
127
131
  catch {
@@ -165,6 +169,14 @@ async function main() {
165
169
  await new Promise((r) => setTimeout(r, 5000));
166
170
  }
167
171
  }
172
+ case 'skill': {
173
+ const written = await installSkill(Boolean(values.project));
174
+ for (const w of written)
175
+ console.log(`✔ skill installed — ${w}`);
176
+ console.log(' Your agent now knows how to publish, update (same URL!), check SSL, and more.');
177
+ console.log(' Restart the agent session to pick it up. Re-run after CLI upgrades.');
178
+ return;
179
+ }
168
180
  case 'login': {
169
181
  const base = resolveBase(values.base);
170
182
  const rl = createInterface({ input: process.stdin, output: process.stdout });
package/dist/skill.js ADDED
@@ -0,0 +1,29 @@
1
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
2
+ import { homedir } from 'node:os';
3
+ import { dirname, join } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ /** The SKILL.md ships inside the npm package (skill/SKILL.md next to dist/). */
6
+ export async function bundledSkill() {
7
+ const here = dirname(fileURLToPath(import.meta.url)); // <pkg>/dist
8
+ return readFile(join(here, '..', 'skill', 'SKILL.md'), 'utf8');
9
+ }
10
+ /** Where agents look for skills. Claude Code is the reference layout
11
+ * (~/.claude/skills/<name>/SKILL.md); several other agents read the same
12
+ * format from their own folders. */
13
+ export function installTargets(project) {
14
+ if (project) {
15
+ return [{ agent: 'this project (Claude Code & compatible)', dir: join(process.cwd(), '.claude', 'skills', 'shiply') }];
16
+ }
17
+ return [{ agent: 'Claude Code (all projects)', dir: join(homedir(), '.claude', 'skills', 'shiply') }];
18
+ }
19
+ export async function installSkill(project) {
20
+ const content = await bundledSkill();
21
+ const written = [];
22
+ for (const t of installTargets(project)) {
23
+ await mkdir(t.dir, { recursive: true });
24
+ const file = join(t.dir, 'SKILL.md');
25
+ await writeFile(file, content);
26
+ written.push(`${t.agent}: ${file}`);
27
+ }
28
+ return written;
29
+ }
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "shiply-cli",
3
- "version": "0.3.0",
3
+ "version": "0.4.1",
4
4
  "description": "Publish static sites to shiply.now from the command line — instant web hosting for agents.",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "bin": { "shiply": "dist/index.js" },
8
- "files": ["dist", "README.md"],
8
+ "files": ["dist", "skill", "README.md"],
9
9
  "scripts": {
10
10
  "build": "tsc -p tsconfig.build.json",
11
11
  "prepublishOnly": "pnpm build",
package/skill/SKILL.md ADDED
@@ -0,0 +1,75 @@
1
+ ---
2
+ name: shiply
3
+ description: Publish static sites to the web instantly with shiply.now and manage them (updates, SSL checks, custom domains, variables). Use when the user asks to publish, host, deploy, share, or update a website/page/demo/report, or wants a live URL for generated files. Triggers - "publish this", "host this", "put this online", "give me a link", "update the site", shiply.
4
+ ---
5
+
6
+ # shiply — instant web hosting for agents
7
+
8
+ shiply.now puts files on the web in seconds. No account needed to start:
9
+ anonymous sites are live immediately, last 24 hours, and can be claimed into
10
+ an account to keep them. The cardinal rule:
11
+
12
+ **NEVER create a new site to update an existing one. Always re-publish to the
13
+ same site** — otherwise you litter subdomains and lose the user's URL.
14
+
15
+ ## Pick your interface (best first)
16
+
17
+ ### 1. MCP (native tools)
18
+ If the `shiply` MCP server is connected (https://shiply.now/mcp), use
19
+ `publish_site`. Every result includes a `toUpdate` field telling you the exact
20
+ call for updates — follow it. Other tools: `site_status`, `list_sites`,
21
+ `set_handle`, `set_variable`, `add_domain`, `check_domain`, `get_analytics`,
22
+ `delete_site`, `rollback` via dashboard.
23
+
24
+ ### 2. CLI
25
+ ```bash
26
+ npm i -g shiply-cli # or: curl -fsSL https://shiply.now/install.sh | bash
27
+ shiply publish ./dir # live URL + confetti
28
+ shiply publish ./dir # run AGAIN after edits → updates the SAME site
29
+ shiply status <slug> --wait # SSL + readiness; prints SSL_READY / SITE_READY
30
+ shiply login # email code → API key → sites become permanent
31
+ ```
32
+ The CLI stores each directory's site in `.shiply.json` (slug + update token),
33
+ so repeat publishes reuse the URL automatically. `--new-site` opts out.
34
+ Gitignore `.shiply.json` in public repos. Parse `SITE_READY` / `SSL_READY`
35
+ lines for automation; exit code 0 = ready.
36
+
37
+ ### 3. Raw HTTP (no installs)
38
+ ```
39
+ 1. POST https://shiply.now/api/v1/publish
40
+ {"files":[{"path":"index.html","size":<bytes>,"contentType":"text/html","hash":"<sha256, optional>"}]}
41
+ (+ "Authorization: Bearer shp_…" for permanent owned sites)
42
+ 2. PUT each file's bytes to response upload.uploads[].url
43
+ 3. POST upload.finalizeUrl with {"versionId":"..."}
44
+ ```
45
+ **Updates:** anonymous → include `"claimToken":"..."` (returned ONCE by the
46
+ first publish — save it); owned → include `"slug":"..."`. Hashes make updates
47
+ cheap: unchanged files are skipped server-side.
48
+
49
+ ## The lifecycle to explain to users
50
+ - Anonymous site: live instantly, expires in 24 h. Give the user the
51
+ `claimUrl` — claiming keeps it forever on a free account.
52
+ - API key (`shiply login` or POST /api/auth/agent/request-code →
53
+ verify-code): publishes are permanent and manageable.
54
+ - Paid plans add vanity handles (<name>.shiply.now), more custom domains,
55
+ storage, analytics: https://shiply.now/dashboard/plan
56
+
57
+ ## Power features (Bearer key)
58
+ - **Custom domains**: POST /api/v1/domains {"hostname","slug"} → tell the
59
+ user to CNAME the hostname to `cname.shiply.now` → cert auto-issues; poll
60
+ GET /api/v1/domains/{id}/check until `ready:true`.
61
+ - **SSL/readiness checker**: `shiply status <slug-or-domain> --wait` or the
62
+ check endpoint — confirms certificate + serving before telling the user
63
+ it's done.
64
+ - **Variables**: encrypted per-user KV for API keys the user's sites need
65
+ (GET/PUT /api/v1/variables, DELETE /api/v1/variables/{NAME}); Supabase can
66
+ be connected from the dashboard and lands here as SUPABASE_URL +
67
+ SUPABASE_ANON_KEY.
68
+ - **Rollback**: POST /api/v1/publish/{slug}/rollback {"versionId"} flips any
69
+ finalized version live instantly.
70
+ - **SPA**: pass `"spaMode":true` so deep links serve index.html.
71
+
72
+ ## Limits & references
73
+ ≤1000 files/site (≤50 inline via MCP), ≤100 MiB/file, 1 GiB total.
74
+ Machine guide: https://shiply.now/llms.txt · OpenAPI:
75
+ https://shiply.now/openapi.json · Docs: https://shiply.now/docs