shiply-cli 0.20.2 → 0.21.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.
@@ -0,0 +1,48 @@
1
+ import { api, resolveBase } from './publish.js';
2
+ import { loadApiKey } from './config.js';
3
+ const hdr = (apiKey) => ({
4
+ 'content-type': 'application/json',
5
+ ...(apiKey ? { authorization: `Bearer ${apiKey}` } : {}),
6
+ });
7
+ /** Turn a `--client "<name-or-email>"` flag into the API's client object.
8
+ * An '@' means email (create-or-find by it); otherwise a display name. */
9
+ export function parseClientArg(value) {
10
+ const v = value?.trim();
11
+ if (!v)
12
+ return undefined;
13
+ return v.includes('@') ? { clientEmail: v } : { clientName: v };
14
+ }
15
+ /** `shiply client ls | show <id>` — read-only view of your clients. */
16
+ export async function runClient(args, opts) {
17
+ const sub = args[0] ?? 'ls';
18
+ const base = resolveBase(opts.base);
19
+ const apiKey = opts.key ?? (await loadApiKey());
20
+ if (!apiKey)
21
+ throw new Error('`shiply client` needs an API key — run `shiply login` or pass --key');
22
+ if (sub === 'ls') {
23
+ const { clients } = await api(`${base}/api/v1/clients`, { headers: hdr(apiKey) });
24
+ if (clients.length === 0) {
25
+ console.log(' no clients yet — pass --client on publish or create an intake to add one');
26
+ return;
27
+ }
28
+ for (const c of clients) {
29
+ const meta = [c.company, c.email].filter(Boolean).join(' · ');
30
+ console.log(` ${c.id} ${c.name}${meta ? ` (${meta})` : ''} [${c.status}]`);
31
+ }
32
+ return;
33
+ }
34
+ if (sub === 'show') {
35
+ const id = args[1];
36
+ if (!id)
37
+ throw new Error('usage: shiply client show <id>');
38
+ const r = await api(`${base}/api/v1/clients/${encodeURIComponent(id)}`, { headers: hdr(apiKey) });
39
+ console.log(`${r.client.name}${r.client.company ? ` · ${r.client.company}` : ''}`);
40
+ if (r.client.email)
41
+ console.log(` ${r.client.email}`);
42
+ console.log(` sites (${r.sites.length}): ${r.sites.map((s) => s.slug).join(', ') || '—'}`);
43
+ console.log(` folders (${r.drives.length}): ${r.drives.map((d) => d.name).join(', ') || '—'}`);
44
+ console.log(` projects (${r.projects.length}): ${r.projects.map((p) => `${p.label} [${p.status}]`).join(', ') || '—'}`);
45
+ return;
46
+ }
47
+ throw new Error(`unknown client command '${sub}' — try: ls, show <id>`);
48
+ }
package/dist/index.js CHANGED
@@ -16,6 +16,7 @@ import { dbAttach, dbBranch, dbBranches, dbCreate, dbDelete, dbDeleteBranch, dbL
16
16
  import { driveGet, driveLs, drivePublish, drivePut, driveRm } from './drive.js';
17
17
  import { domainAdd, domainConnect, domainLs, domainPrimary, domainSubAdd, domainSync } from './domain.js';
18
18
  import { runProject } from './projects.js';
19
+ import { runClient, parseClientArg } from './clients.js';
19
20
  import { runListing } from './listings.js';
20
21
  import { runSendingDomain } from './sending-domains.js';
21
22
  import { contractAmend, contractDraft, contractList, contractPdf, contractRetract, contractSend, contractShow, } from './contract.js';
@@ -82,6 +83,9 @@ Usage:
82
83
  Customer-intake projects + AI brief generator
83
84
  shiply project create <label> [--customer-email <e>] [--customer-name <n>]
84
85
  Returns the intake URL to share with the customer
86
+ shiply client <ls|show <id>> Your clients (freelancer view): customer-360 roll-up
87
+ shiply publish <dir> --client "<name|email>"
88
+ Group the site under a client (create-or-finds by email)
85
89
  shiply listing <ls|create|rm> Marketplace listings (sell built sites)
86
90
  shiply listing create <site-slug> --price <cents> --jurisdiction "<region>"
87
91
  Requires Stripe Connect onboarding (see MCP get_connect_status)
@@ -257,6 +261,7 @@ async function main() {
257
261
  'no-notify': { type: 'boolean' },
258
262
  'notify-to': { type: 'string' },
259
263
  from: { type: 'string' },
264
+ client: { type: 'string' },
260
265
  help: { type: 'boolean', short: 'h' },
261
266
  version: { type: 'boolean', short: 'v' },
262
267
  },
@@ -347,6 +352,8 @@ async function main() {
347
352
  slug,
348
353
  attachDatabaseId,
349
354
  previewBranchDbId,
355
+ // Group under a client (owned publishes only). --client "<name|email>"
356
+ client: apiKey ? parseClientArg(values.client) : undefined,
350
357
  });
351
358
  await writeState(stateDir, {
352
359
  slug: res.slug,
@@ -734,6 +741,10 @@ async function main() {
734
741
  await runProject(positionals.slice(1));
735
742
  break;
736
743
  }
744
+ case 'client': {
745
+ await runClient(positionals.slice(1), { base: values.base, key: values.key });
746
+ break;
747
+ }
737
748
  case 'listing': {
738
749
  await runListing(positionals.slice(1));
739
750
  break;
package/dist/publish.js CHANGED
@@ -42,6 +42,7 @@ export async function publish(dir, opts = {}) {
42
42
  ...(opts.slug ? { slug: opts.slug } : {}),
43
43
  ...(opts.attachDatabaseId ? { attachDatabaseId: opts.attachDatabaseId } : {}),
44
44
  ...(opts.previewBranchDbId ? { previewBranchDbId: opts.previewBranchDbId } : {}),
45
+ ...(opts.client ? { client: opts.client } : {}),
45
46
  }),
46
47
  });
47
48
  await uploadAll(dir, created.upload.uploads);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shiply-cli",
3
- "version": "0.20.2",
3
+ "version": "0.21.0",
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",
package/skill/SKILL.md CHANGED
@@ -18,7 +18,7 @@ description: "shiply is the production backend for anything an agent builds —
18
18
  > fresh — your cached copy may be missing features like databases, projects,
19
19
  > marketplace, or sending domains.
20
20
  >
21
- > **Last updated: 2026-06-27**
21
+ > **Last updated: 2026-06-28**
22
22
 
23
23
  ---
24
24
 
@@ -658,6 +658,21 @@ CLI: `shiply project ls · shiply project create <label> [--customer-email <e>]
658
658
 
659
659
  Docs: https://shiply.now/docs/projects
660
660
 
661
+ **Group work by client (optional, for freelancers).** Building sites for
662
+ clients? Pass an optional `client` (a name or email) on any `publish_site`,
663
+ `create_drive`, or `create_project` call — shiply create-or-finds the client
664
+ by email and files everything you ship for them (sites, drop-box, intake,
665
+ contracts) under one customer view in the dashboard. A project's customer **is**
666
+ the site's client (same email-keyed identity), so `create_project` already
667
+ links automatically whenever you pass `customerEmail` — no extra step. There is
668
+ no `create_client` tool and never a CRM step: the client record self-assembles
669
+ from the calls you already make. `publish_site` echoes the `resolvedClientId` —
670
+ reuse it across the session so drive/intake/contract all land under the same
671
+ client. List calls take an optional `clientId` filter
672
+ (`list_sites`/`list_drives`/`list_projects`). Omit `client` for personal
673
+ projects; shiply works identically. CLI: `--client "<name-or-email>"` on
674
+ publish/drive/project, plus `shiply client ls|show`.
675
+
661
676
  ## Contracts — draft, sign, amend (extends Projects)
662
677
 
663
678
  Once a project reaches `brief_ready`, the dev drafts a signed contract from