primitive-admin 1.1.0-alpha.4 → 1.1.0-alpha.41
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/README.md +325 -31
- package/assets/skill/skills/primitive-platform/SKILL.md +281 -0
- package/dist/bin/primitive.d.ts +2 -0
- package/dist/bin/primitive.js +268 -16
- package/dist/bin/primitive.js.map +1 -1
- package/dist/src/commands/admins.d.ts +2 -0
- package/dist/src/commands/admins.js +123 -18
- package/dist/src/commands/admins.js.map +1 -1
- package/dist/src/commands/analytics.d.ts +2 -0
- package/dist/src/commands/analytics.js +464 -55
- package/dist/src/commands/analytics.js.map +1 -1
- package/dist/src/commands/apps.d.ts +2 -0
- package/dist/src/commands/apps.js +55 -13
- package/dist/src/commands/apps.js.map +1 -1
- package/dist/src/commands/auth.d.ts +2 -0
- package/dist/src/commands/auth.js +177 -7
- package/dist/src/commands/auth.js.map +1 -1
- package/dist/src/commands/blob-buckets.d.ts +2 -0
- package/dist/src/commands/blob-buckets.js +422 -0
- package/dist/src/commands/blob-buckets.js.map +1 -0
- package/dist/src/commands/catalog.d.ts +2 -0
- package/dist/src/commands/catalog.js +34 -35
- package/dist/src/commands/catalog.js.map +1 -1
- package/dist/src/commands/collection-type-configs.d.ts +2 -0
- package/dist/src/commands/collection-type-configs.js +178 -0
- package/dist/src/commands/collection-type-configs.js.map +1 -0
- package/dist/src/commands/collections.d.ts +2 -0
- package/dist/src/commands/collections.js +515 -0
- package/dist/src/commands/collections.js.map +1 -0
- package/dist/src/commands/comparisons.d.ts +2 -0
- package/dist/src/commands/comparisons.js +6 -6
- package/dist/src/commands/comparisons.js.map +1 -1
- package/dist/src/commands/cron-triggers.d.ts +2 -0
- package/dist/src/commands/cron-triggers.js +357 -0
- package/dist/src/commands/cron-triggers.js.map +1 -0
- package/dist/src/commands/database-types.d.ts +2 -0
- package/dist/src/commands/database-types.js +471 -0
- package/dist/src/commands/database-types.js.map +1 -0
- package/dist/src/commands/databases.d.ts +2 -0
- package/dist/src/commands/databases.js +1462 -83
- package/dist/src/commands/databases.js.map +1 -1
- package/dist/src/commands/documents.d.ts +2 -0
- package/dist/src/commands/documents.js +518 -11
- package/dist/src/commands/documents.js.map +1 -1
- package/dist/src/commands/email-templates.d.ts +2 -0
- package/dist/src/commands/email-templates.js +281 -0
- package/dist/src/commands/email-templates.js.map +1 -0
- package/dist/src/commands/env.d.ts +12 -0
- package/dist/src/commands/env.js +260 -0
- package/dist/src/commands/env.js.map +1 -0
- package/dist/src/commands/group-type-configs.d.ts +2 -0
- package/dist/src/commands/group-type-configs.js +181 -0
- package/dist/src/commands/group-type-configs.js.map +1 -0
- package/dist/src/commands/groups.d.ts +2 -0
- package/dist/src/commands/groups.js +48 -45
- package/dist/src/commands/groups.js.map +1 -1
- package/dist/src/commands/guides.d.ts +159 -0
- package/dist/src/commands/guides.js +449 -35
- package/dist/src/commands/guides.js.map +1 -1
- package/dist/src/commands/init.d.ts +17 -0
- package/dist/src/commands/init.js +811 -201
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/integrations.d.ts +2 -0
- package/dist/src/commands/integrations.js +533 -46
- package/dist/src/commands/integrations.js.map +1 -1
- package/dist/src/commands/llm.d.ts +2 -0
- package/dist/src/commands/llm.js +4 -2
- package/dist/src/commands/llm.js.map +1 -1
- package/dist/src/commands/metadata.d.ts +2 -0
- package/dist/src/commands/metadata.js +194 -0
- package/dist/src/commands/metadata.js.map +1 -0
- package/dist/src/commands/prompts.d.ts +2 -0
- package/dist/src/commands/prompts.js +50 -52
- package/dist/src/commands/prompts.js.map +1 -1
- package/dist/src/commands/rule-sets.d.ts +3 -0
- package/dist/src/commands/rule-sets.js +388 -0
- package/dist/src/commands/rule-sets.js.map +1 -0
- package/dist/src/commands/scripts.d.ts +2 -0
- package/dist/src/commands/scripts.js +667 -0
- package/dist/src/commands/scripts.js.map +1 -0
- package/dist/src/commands/secrets.d.ts +2 -0
- package/dist/src/commands/secrets.js +108 -0
- package/dist/src/commands/secrets.js.map +1 -0
- package/dist/src/commands/skill.d.ts +2 -0
- package/dist/src/commands/skill.js +29 -0
- package/dist/src/commands/skill.js.map +1 -0
- package/dist/src/commands/sync.d.ts +175 -0
- package/dist/src/commands/sync.js +4412 -233
- package/dist/src/commands/sync.js.map +1 -1
- package/dist/src/commands/tokens.d.ts +2 -0
- package/dist/src/commands/tokens.js +122 -19
- package/dist/src/commands/tokens.js.map +1 -1
- package/dist/src/commands/users.d.ts +2 -0
- package/dist/src/commands/users.js +440 -22
- package/dist/src/commands/users.js.map +1 -1
- package/dist/src/commands/waitlist.d.ts +2 -0
- package/dist/src/commands/waitlist.js +10 -10
- package/dist/src/commands/waitlist.js.map +1 -1
- package/dist/src/commands/webhooks.d.ts +2 -0
- package/dist/src/commands/webhooks.js +391 -0
- package/dist/src/commands/webhooks.js.map +1 -0
- package/dist/src/commands/workflows.d.ts +49 -0
- package/dist/src/commands/workflows.js +1009 -123
- package/dist/src/commands/workflows.js.map +1 -1
- package/dist/src/lib/api-client.d.ts +1283 -0
- package/dist/src/lib/api-client.js +1129 -83
- package/dist/src/lib/api-client.js.map +1 -1
- package/dist/src/lib/auth-flow.d.ts +8 -0
- package/dist/src/lib/block-layout.d.ts +160 -0
- package/dist/src/lib/block-layout.js +451 -0
- package/dist/src/lib/block-layout.js.map +1 -0
- package/dist/src/lib/cli-manifest.d.ts +60 -0
- package/dist/src/lib/cli-manifest.js +70 -0
- package/dist/src/lib/cli-manifest.js.map +1 -0
- package/dist/src/lib/config.d.ts +53 -0
- package/dist/src/lib/config.js +92 -53
- package/dist/src/lib/config.js.map +1 -1
- package/dist/src/lib/confirm-prompt.d.ts +83 -0
- package/dist/src/lib/confirm-prompt.js +110 -0
- package/dist/src/lib/confirm-prompt.js.map +1 -0
- package/dist/src/lib/constants.d.ts +2 -0
- package/dist/src/lib/constants.js +3 -0
- package/dist/src/lib/constants.js.map +1 -0
- package/dist/src/lib/crash-handlers.d.ts +20 -0
- package/dist/src/lib/crash-handlers.js +49 -0
- package/dist/src/lib/crash-handlers.js.map +1 -0
- package/dist/src/lib/credentials-store.d.ts +79 -0
- package/dist/src/lib/credentials-store.js +307 -0
- package/dist/src/lib/credentials-store.js.map +1 -0
- package/dist/src/lib/csv.d.ts +48 -0
- package/dist/src/lib/csv.js +177 -0
- package/dist/src/lib/csv.js.map +1 -0
- package/dist/src/lib/db-codegen/dbFingerprint.d.ts +10 -0
- package/dist/src/lib/db-codegen/dbFingerprint.js +17 -0
- package/dist/src/lib/db-codegen/dbFingerprint.js.map +1 -0
- package/dist/src/lib/db-codegen/dbGenerator.d.ts +111 -0
- package/dist/src/lib/db-codegen/dbGenerator.js +371 -0
- package/dist/src/lib/db-codegen/dbGenerator.js.map +1 -0
- package/dist/src/lib/db-codegen/dbNaming.d.ts +45 -0
- package/dist/src/lib/db-codegen/dbNaming.js +104 -0
- package/dist/src/lib/db-codegen/dbNaming.js.map +1 -0
- package/dist/src/lib/db-codegen/dbTemplates.d.ts +97 -0
- package/dist/src/lib/db-codegen/dbTemplates.js +159 -0
- package/dist/src/lib/db-codegen/dbTemplates.js.map +1 -0
- package/dist/src/lib/db-codegen/dbTsTypes.d.ts +78 -0
- package/dist/src/lib/db-codegen/dbTsTypes.js +112 -0
- package/dist/src/lib/db-codegen/dbTsTypes.js.map +1 -0
- package/dist/src/lib/env-resolver.d.ts +62 -0
- package/dist/src/lib/env-resolver.js +121 -0
- package/dist/src/lib/env-resolver.js.map +1 -0
- package/dist/src/lib/fetch.d.ts +5 -0
- package/dist/src/lib/generated-allowlist.d.ts +28 -0
- package/dist/src/lib/generated-allowlist.js +220 -0
- package/dist/src/lib/generated-allowlist.js.map +1 -0
- package/dist/src/lib/init-config.d.ts +59 -0
- package/dist/src/lib/init-config.js +113 -0
- package/dist/src/lib/init-config.js.map +1 -0
- package/dist/src/lib/migration-nag.d.ts +49 -0
- package/dist/src/lib/migration-nag.js +163 -0
- package/dist/src/lib/migration-nag.js.map +1 -0
- package/dist/src/lib/output.d.ts +86 -0
- package/dist/src/lib/output.js +150 -8
- package/dist/src/lib/output.js.map +1 -1
- package/dist/src/lib/paginate.d.ts +33 -0
- package/dist/src/lib/paginate.js +42 -0
- package/dist/src/lib/paginate.js.map +1 -0
- package/dist/src/lib/project-config.d.ts +97 -0
- package/dist/src/lib/project-config.js +209 -0
- package/dist/src/lib/project-config.js.map +1 -0
- package/dist/src/lib/query-operators.d.ts +43 -0
- package/dist/src/lib/query-operators.js +80 -0
- package/dist/src/lib/query-operators.js.map +1 -0
- package/dist/src/lib/refresh-admin-credentials.d.ts +65 -0
- package/dist/src/lib/refresh-admin-credentials.js +103 -0
- package/dist/src/lib/refresh-admin-credentials.js.map +1 -0
- package/dist/src/lib/resolve-platform.d.ts +45 -0
- package/dist/src/lib/resolve-platform.js +43 -0
- package/dist/src/lib/resolve-platform.js.map +1 -0
- package/dist/src/lib/skill-installer.d.ts +23 -0
- package/dist/src/lib/skill-installer.js +135 -0
- package/dist/src/lib/skill-installer.js.map +1 -0
- package/dist/src/lib/snapshots.d.ts +99 -0
- package/dist/src/lib/snapshots.js +357 -0
- package/dist/src/lib/snapshots.js.map +1 -0
- package/dist/src/lib/sync-paths.d.ts +72 -0
- package/dist/src/lib/sync-paths.js +130 -0
- package/dist/src/lib/sync-paths.js.map +1 -0
- package/dist/src/lib/template.d.ts +97 -0
- package/dist/src/lib/template.js +336 -62
- package/dist/src/lib/template.js.map +1 -1
- package/dist/src/lib/token-inject.d.ts +56 -0
- package/dist/src/lib/token-inject.js +204 -0
- package/dist/src/lib/token-inject.js.map +1 -0
- package/dist/src/lib/toml-database-config.d.ts +152 -0
- package/dist/src/lib/toml-database-config.js +600 -0
- package/dist/src/lib/toml-database-config.js.map +1 -0
- package/dist/src/lib/toml-metadata-config.d.ts +139 -0
- package/dist/src/lib/toml-metadata-config.js +427 -0
- package/dist/src/lib/toml-metadata-config.js.map +1 -0
- package/dist/src/lib/toml-params-validator.d.ts +129 -0
- package/dist/src/lib/toml-params-validator.js +298 -0
- package/dist/src/lib/toml-params-validator.js.map +1 -0
- package/dist/src/lib/version-check.d.ts +10 -0
- package/dist/src/lib/version-check.js +172 -0
- package/dist/src/lib/version-check.js.map +1 -0
- package/dist/src/lib/workflow-apply.d.ts +66 -0
- package/dist/src/lib/workflow-apply.js +117 -0
- package/dist/src/lib/workflow-apply.js.map +1 -0
- package/dist/src/lib/workflow-fragments.d.ts +41 -0
- package/dist/src/lib/workflow-fragments.js +121 -0
- package/dist/src/lib/workflow-fragments.js.map +1 -0
- package/dist/src/lib/workflow-toml-validator.d.ts +95 -0
- package/dist/src/lib/workflow-toml-validator.js +323 -0
- package/dist/src/lib/workflow-toml-validator.js.map +1 -0
- package/dist/src/types/index.d.ts +520 -0
- package/dist/src/validators.d.ts +64 -0
- package/dist/src/validators.js +63 -0
- package/dist/src/validators.js.map +1 -0
- package/package.json +18 -4
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: primitive-platform
|
|
3
|
+
description: >
|
|
4
|
+
Expert guide for building applications on the Primitive platform. MUST be used whenever the user
|
|
5
|
+
is writing code that uses js-bao, js-bao-wss-client, primitive-app components, or any Primitive
|
|
6
|
+
platform feature (documents, databases, workflows, prompts, integrations, blobs, authentication,
|
|
7
|
+
users/groups). Also trigger whenever about to run any `primitive` CLI command (e.g., primitive sync, primitive integrations, primitive apps, primitive env) to ensure Step 0 CLI verification is performed first. After writing or modifying code that touches Primitive
|
|
8
|
+
APIs, this skill cross-references the implementation against official guides and automatically
|
|
9
|
+
corrects common mistakes. Use this skill even if the user doesn't explicitly ask for it —
|
|
10
|
+
any Primitive-related code should be validated against current best practices.
|
|
11
|
+
allowed-tools: Bash, Read, Edit, Write, Glob, Grep, Agent
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Primitive Platform Development Guide
|
|
15
|
+
|
|
16
|
+
You are an expert on the Primitive platform. Your job is to help developers write correct,
|
|
17
|
+
idiomatic Primitive code by leveraging the CLI's built-in guide system and enforcing best practices.
|
|
18
|
+
|
|
19
|
+
**The CLI guides are the single source of truth.** Never hardcode or memorize guide content —
|
|
20
|
+
always fetch the latest from the CLI.
|
|
21
|
+
|
|
22
|
+
## Step 0: Verify CLI Configuration
|
|
23
|
+
|
|
24
|
+
The Primitive CLI is **project-scoped**, and project mode is **strongly preferred** for any work
|
|
25
|
+
inside a repo. Each project has a `.primitive/config.json` (committed to the repo) that defines
|
|
26
|
+
named environments (`dev`, `prod`, `staging`, …), where each environment binds an `apiUrl` and
|
|
27
|
+
(optionally) an `appId`. Per-environment auth tokens live in `.primitive/credentials.json`
|
|
28
|
+
(gitignored). There is no global "currently active app" — the active environment determines the
|
|
29
|
+
server *and* the app.
|
|
30
|
+
|
|
31
|
+
The legacy global fallback (`~/.primitive/credentials.json`) exists only for one-off use outside a
|
|
32
|
+
project. **Inside a project, treat its absence as a setup gap to fix, not a mode to operate in.**
|
|
33
|
+
|
|
34
|
+
**Before running any CLI commands**, your *first* check is whether the project is in project mode:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
ls .primitive/config.json # exists at project root or any ancestor?
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The two branches below are not equivalent — pick the one that matches reality and follow it.
|
|
41
|
+
|
|
42
|
+
### Branch A — `.primitive/config.json` exists (project mode)
|
|
43
|
+
|
|
44
|
+
The active environment is resolved in this order:
|
|
45
|
+
1. `--env <name>` flag on the command
|
|
46
|
+
2. `PRIMITIVE_ENV` environment variable
|
|
47
|
+
3. `defaultEnvironment` in `.primitive/config.json`
|
|
48
|
+
4. The sole environment, if exactly one is defined
|
|
49
|
+
|
|
50
|
+
Confirm you're targeting the correct environment:
|
|
51
|
+
|
|
52
|
+
1. **Read the CLI header.** Every command prints `Env | App | Server` at the top of its output —
|
|
53
|
+
verify these match the project's intended target.
|
|
54
|
+
2. **Inspect the project config:**
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
primitive env list # All environments (default marked with *)
|
|
58
|
+
primitive env show # Details for the currently-resolved env
|
|
59
|
+
primitive whoami # Authenticated user + resolved server/app
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**To switch environments** for a one-off command, pass `--env <name>`. To change the project
|
|
63
|
+
default, run `primitive env use <name>`. To switch the *app* an env points at, edit the env's
|
|
64
|
+
`appId` in `.primitive/config.json` (or re-run `primitive env add`). `primitive use <app>` is a
|
|
65
|
+
no-op when the active env already pins an `appId`.
|
|
66
|
+
|
|
67
|
+
### Branch B — no `.primitive/config.json` (project mode NOT set up)
|
|
68
|
+
|
|
69
|
+
Without project config the CLI silently falls back to global state in `~/.primitive/credentials.json`
|
|
70
|
+
(legacy mode). Commands run against whatever app/server happens to be globally active — which the
|
|
71
|
+
agent didn't set and the user may have forgotten about. **This is a footgun, not a supported way to
|
|
72
|
+
work inside a project.** Do not proceed silently, and do not treat the global fallback as the
|
|
73
|
+
default path.
|
|
74
|
+
|
|
75
|
+
**Your default action is to set up project mode.** Stop and prompt the user to create the project
|
|
76
|
+
config before doing anything else. Don't bury the recommendation behind an equal-weight "or proceed
|
|
77
|
+
against global state" option — make setting up project config the clear, recommended next step.
|
|
78
|
+
|
|
79
|
+
First gather the context you'll propose (so the prompt is concrete, not abstract):
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
primitive whoami # current global server + app, if any — shows what the fallback WOULD target
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Then prompt the user, e.g.:
|
|
86
|
+
|
|
87
|
+
> "This project has no `.primitive/config.json`, so the CLI isn't in project mode. I recommend
|
|
88
|
+
> setting up project-scoped config so this repo pins its own environment instead of relying on your
|
|
89
|
+
> global state (currently `<server>` / `<app from whoami>`, which I didn't set). I'll add an env
|
|
90
|
+
> with:
|
|
91
|
+
>
|
|
92
|
+
> ```bash
|
|
93
|
+
> primitive env add dev --api-url <url> --app-id <id>
|
|
94
|
+
> ```
|
|
95
|
+
>
|
|
96
|
+
> Does this look right, or should I adjust the env name / URL / app?"
|
|
97
|
+
|
|
98
|
+
If you need the user to pick the env name, server, or app, ask them. Confirm the values before
|
|
99
|
+
running `env add` — but the question to resolve is *which* project config to create, not *whether*
|
|
100
|
+
to create one.
|
|
101
|
+
|
|
102
|
+
`primitive env add` is additive and safe — it only writes an entry to `.primitive/config.json`
|
|
103
|
+
(creating the file if needed). It does not touch source code, create apps on the server, or install
|
|
104
|
+
dependencies.
|
|
105
|
+
|
|
106
|
+
**Only fall back to global state if the user explicitly declines project setup** after you've
|
|
107
|
+
recommended it. Even then, name the exact server/app the command will hit and get clear
|
|
108
|
+
confirmation before running anything mutating (`primitive sync push`, `primitive apps create`, etc.).
|
|
109
|
+
A read-only command (`whoami`, `guides list`) against global state is fine while you're still
|
|
110
|
+
working out the config.
|
|
111
|
+
|
|
112
|
+
Do not rely on `.env` files like `PRIMITIVE_API_URL` to control CLI targeting — those are not
|
|
113
|
+
read by the CLI in project mode, and the project config is the source of truth.
|
|
114
|
+
|
|
115
|
+
**Why this matters:** If the CLI is pointed at the wrong environment (e.g., prod instead of dev),
|
|
116
|
+
commands like `primitive sync push` will modify the wrong server. Silent fallback to global state
|
|
117
|
+
makes this exact mistake easy to commit. Setting up project config is the durable fix — verify and
|
|
118
|
+
surface before running mutating operations.
|
|
119
|
+
|
|
120
|
+
## Step 1: Discover Available Guides
|
|
121
|
+
|
|
122
|
+
Before writing or reviewing any Primitive code, run:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
primitive guides list
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
This returns the full list of available guide topics with descriptions, keywords, and use cases.
|
|
129
|
+
The `COMBINATIONS` column shows which `(language, platform)` variants each guide is available in
|
|
130
|
+
(e.g. `ts; swift`). Use this output to determine which guides are relevant to the current task —
|
|
131
|
+
and which language/platform variant to request in Step 2.
|
|
132
|
+
|
|
133
|
+
### Determine the project's language and platform
|
|
134
|
+
|
|
135
|
+
Figure out what the project you're working in targets, then request the matching variant when
|
|
136
|
+
fetching guides:
|
|
137
|
+
|
|
138
|
+
- A `Package.swift`, `*.xcodeproj`, or `project.yml` → `--language swift` (plus `--platform ios`
|
|
139
|
+
or `--platform macos` as appropriate).
|
|
140
|
+
- A Vite/React/Node web app (`package.json`, `js-bao-wss-client`) → `--language ts --platform web`.
|
|
141
|
+
|
|
142
|
+
If you can't tell, omit the flags — every guide has a default variant, so a bare
|
|
143
|
+
`primitive guides get <topic>` always returns something useful.
|
|
144
|
+
|
|
145
|
+
## Step 2: Fetch the Relevant Guides
|
|
146
|
+
|
|
147
|
+
For each relevant topic identified in Step 1, fetch the full guide, passing the project's
|
|
148
|
+
language/platform so you get the right variant:
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
primitive guides get <topic> --language <ts|swift> --platform <web|ios|macos>
|
|
152
|
+
# or, when the project's language/platform is unknown or doesn't matter:
|
|
153
|
+
primitive guides get <topic>
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
`--language` accepts aliases (`typescript`/`javascript`/`js` → `ts`). These flags **never fail**:
|
|
157
|
+
an unknown value or an unavailable combination falls back to the guide's default variant rather
|
|
158
|
+
than erroring, so it's always safe to pass your best guess.
|
|
159
|
+
|
|
160
|
+
**Always fetch guide(s) BEFORE writing code.** If multiple features are involved, fetch multiple
|
|
161
|
+
guides. The guides contain:
|
|
162
|
+
- Complete API documentation with method signatures
|
|
163
|
+
- Working code examples in the requested language (e.g. TypeScript or Swift)
|
|
164
|
+
- Common patterns and anti-patterns
|
|
165
|
+
- Configuration examples (TOML files for `primitive sync`)
|
|
166
|
+
- Decision frameworks for architecture choices
|
|
167
|
+
|
|
168
|
+
**Do not guess or assume API patterns.** If you're unsure about a method signature, parameter,
|
|
169
|
+
or pattern, fetch the guide. The guides are comprehensive and authoritative.
|
|
170
|
+
|
|
171
|
+
## Step 3: Write Code Following Guide Patterns
|
|
172
|
+
|
|
173
|
+
When writing Primitive code:
|
|
174
|
+
|
|
175
|
+
1. **Follow the patterns from the fetched guides exactly** — method names, argument order, lifecycle patterns
|
|
176
|
+
2. **Use `primitive sync`** for all backend configuration (workflows, prompts, integrations, databases)
|
|
177
|
+
3. **Configuration lives in TOML files** in version control, pushed via `primitive sync push`
|
|
178
|
+
4. **Run `pnpm codegen`** after creating or modifying js-bao models
|
|
179
|
+
|
|
180
|
+
## Step 4: Post-Code Review (Automatic)
|
|
181
|
+
|
|
182
|
+
After writing or modifying Primitive-related code, **automatically perform this review**:
|
|
183
|
+
|
|
184
|
+
### 4a. Identify What Was Written
|
|
185
|
+
Determine which Primitive features the new/modified code touches by scanning for:
|
|
186
|
+
- Import statements from `js-bao`, `js-bao-wss-client`, or `primitive-app`
|
|
187
|
+
- Primitive API calls (documents.open, databases.connect, workflows, etc.)
|
|
188
|
+
- Model definitions, schemas, queries
|
|
189
|
+
- Configuration files (TOML for sync)
|
|
190
|
+
|
|
191
|
+
### 4b. Fetch and Cross-Reference
|
|
192
|
+
Run `primitive guides list` to identify which guides cover the features used, then fetch each one
|
|
193
|
+
in the project's language/platform:
|
|
194
|
+
```bash
|
|
195
|
+
primitive guides get <topic> --language <ts|swift> --platform <web|ios|macos>
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Compare the written code against the guide content:
|
|
199
|
+
- **API usage patterns** — Are methods called correctly with proper arguments?
|
|
200
|
+
- **Lifecycle management** — Are documents opened before queries? Is auth checked first?
|
|
201
|
+
- **Access control** — Are CEL expressions or permissions configured properly?
|
|
202
|
+
- **Anti-patterns** — Does the code do anything the guide explicitly warns against?
|
|
203
|
+
- **Missing steps** — Does the code need `pnpm codegen`, `primitive sync push`, or other follow-up?
|
|
204
|
+
|
|
205
|
+
### 4c. Report and Fix
|
|
206
|
+
If issues are found:
|
|
207
|
+
1. **Explain the issue** — cite the specific guide section that applies
|
|
208
|
+
2. **Show the fix** — provide corrected code
|
|
209
|
+
3. **Apply the fix** — edit the file directly (don't just suggest, actually fix it)
|
|
210
|
+
4. **Note any CLI commands needed** — e.g., `pnpm codegen` or `primitive sync push`
|
|
211
|
+
|
|
212
|
+
If no issues are found, briefly confirm the code follows best practices.
|
|
213
|
+
|
|
214
|
+
## CLI Quick Reference
|
|
215
|
+
|
|
216
|
+
Remind users of these essential commands when relevant:
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
# Verify current configuration (DO THIS FIRST)
|
|
220
|
+
primitive env list # List environments in .primitive/config.json (default marked *)
|
|
221
|
+
primitive env show # Details for the currently-resolved env (api URL, app ID)
|
|
222
|
+
primitive whoami # Authenticated user + resolved server/app
|
|
223
|
+
|
|
224
|
+
# Switching environments
|
|
225
|
+
primitive env use <name> # Change the project's default environment
|
|
226
|
+
primitive --env <name> <command> # One-off override for a single command
|
|
227
|
+
PRIMITIVE_ENV=<name> <command> # Override via env var (useful in scripts/CI)
|
|
228
|
+
|
|
229
|
+
# Setup — existing project (most common: adopting Primitive in an existing repo)
|
|
230
|
+
npm install -g primitive-admin # Install CLI
|
|
231
|
+
primitive env add dev --api-url <url> --app-id <id> # Add env to .primitive/config.json
|
|
232
|
+
primitive env add prod --api-url <url> --app-id <id> # (creates the file if missing)
|
|
233
|
+
primitive login # Authenticate (tokens stored per-env)
|
|
234
|
+
|
|
235
|
+
# Setup — brand-new project (greenfield only)
|
|
236
|
+
primitive init my-new-app # Scaffolds template, creates a new app
|
|
237
|
+
# on the server, runs pnpm install.
|
|
238
|
+
# Do NOT run inside an existing repo.
|
|
239
|
+
|
|
240
|
+
# Guides (the most important commands for development)
|
|
241
|
+
primitive guides list # See all guides: topics, descriptions, available (lang,platform) combinations
|
|
242
|
+
primitive guides get <topic> # Read a guide's default variant
|
|
243
|
+
primitive guides get <topic> --language swift --platform ios # Read a specific language/platform variant
|
|
244
|
+
|
|
245
|
+
# Configuration as Code
|
|
246
|
+
primitive sync init --dir ./config # Initialize config directory
|
|
247
|
+
primitive sync pull --dir ./config # Pull config from server
|
|
248
|
+
primitive sync push --dir ./config # Push config to server
|
|
249
|
+
primitive sync diff --dir ./config # Preview changes before push
|
|
250
|
+
|
|
251
|
+
# Common operations
|
|
252
|
+
primitive apps list # List apps on the active env's server
|
|
253
|
+
primitive apps create "Name" # Create an app (does NOT auto-bind to an env;
|
|
254
|
+
# edit .primitive/config.json or use `env add` to bind)
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
## When the User is Starting a New Feature
|
|
258
|
+
|
|
259
|
+
If the user describes a new feature they want to build:
|
|
260
|
+
|
|
261
|
+
1. **Verify CLI configuration** per Step 0 — confirm the active environment in
|
|
262
|
+
`.primitive/config.json` (and its bound `apiUrl` / `appId`) match the project's intended target
|
|
263
|
+
before running any commands
|
|
264
|
+
2. **Run `primitive guides list`** to discover available topics and their `(language, platform)` combinations
|
|
265
|
+
3. **Identify which guides are relevant** to their feature from the list output
|
|
266
|
+
4. **Fetch those guides** with `primitive guides get <topic> --language <lang> --platform <platform>`
|
|
267
|
+
(using the project's language/platform; omit the flags if unknown)
|
|
268
|
+
5. **Recommend a data modeling approach** based on the guide content. If requirements are unclear or ambiguous, **ask the user clarifying questions before proceeding** — it's much easier to get the data model right upfront than to migrate later
|
|
269
|
+
6. **Outline the implementation steps** referencing specific patterns from the guides
|
|
270
|
+
7. **Write the code** following the patterns exactly
|
|
271
|
+
8. **Review automatically** per Step 4 above
|
|
272
|
+
|
|
273
|
+
## When the User Asks "How Do I...?"
|
|
274
|
+
|
|
275
|
+
For any question about Primitive platform capabilities:
|
|
276
|
+
|
|
277
|
+
1. **Run `primitive guides list`** to find the relevant topic (and its available language/platform combinations)
|
|
278
|
+
2. **Fetch the guide**: `primitive guides get <topic> --language <lang> --platform <platform>` (omit the flags if the language/platform is unknown)
|
|
279
|
+
3. **Answer from the guide content** — don't guess or make up APIs
|
|
280
|
+
4. **Include working code examples** from the guide
|
|
281
|
+
5. **Point the user to the guide** for further reading: "You can see more examples by running `primitive guides get <topic>`"
|
package/dist/bin/primitive.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { Command } from "commander";
|
|
3
|
-
import { readFileSync } from "fs";
|
|
2
|
+
import { Command, CommanderError } from "commander";
|
|
3
|
+
import { readFileSync, writeSync } from "fs";
|
|
4
4
|
import { fileURLToPath } from "url";
|
|
5
5
|
import { dirname, resolve } from "path";
|
|
6
6
|
import { registerInitCommand } from "../src/commands/init.js";
|
|
@@ -9,7 +9,11 @@ import { registerAppsCommands } from "../src/commands/apps.js";
|
|
|
9
9
|
import { registerUsersCommands } from "../src/commands/users.js";
|
|
10
10
|
import { registerWaitlistCommands } from "../src/commands/waitlist.js";
|
|
11
11
|
import { registerIntegrationsCommands } from "../src/commands/integrations.js";
|
|
12
|
+
import { registerWebhooksCommands } from "../src/commands/webhooks.js";
|
|
13
|
+
import { registerCronTriggersCommands } from "../src/commands/cron-triggers.js";
|
|
14
|
+
import { registerMetadataCommands } from "../src/commands/metadata.js";
|
|
12
15
|
import { registerPromptsCommands } from "../src/commands/prompts.js";
|
|
16
|
+
import { registerScriptsCommands } from "../src/commands/scripts.js";
|
|
13
17
|
import { registerWorkflowsCommands } from "../src/commands/workflows.js";
|
|
14
18
|
import { registerAdminsCommands } from "../src/commands/admins.js";
|
|
15
19
|
import { registerCatalogCommands } from "../src/commands/catalog.js";
|
|
@@ -20,47 +24,231 @@ import { registerComparisonsCommands } from "../src/commands/comparisons.js";
|
|
|
20
24
|
import { registerTokensCommands } from "../src/commands/tokens.js";
|
|
21
25
|
import { registerDatabasesCommands } from "../src/commands/databases.js";
|
|
22
26
|
import { registerGroupsCommands } from "../src/commands/groups.js";
|
|
27
|
+
import { registerRuleSetsCommands } from "../src/commands/rule-sets.js";
|
|
28
|
+
import { registerGroupTypeConfigsCommands } from "../src/commands/group-type-configs.js";
|
|
29
|
+
import { registerDatabaseTypesCommands } from "../src/commands/database-types.js";
|
|
23
30
|
import { registerGuidesCommands } from "../src/commands/guides.js";
|
|
24
31
|
import { registerDocumentsCommands } from "../src/commands/documents.js";
|
|
32
|
+
import chalk from "chalk";
|
|
33
|
+
import { registerEmailTemplatesCommands } from "../src/commands/email-templates.js";
|
|
34
|
+
import { registerCollectionsCommands } from "../src/commands/collections.js";
|
|
35
|
+
import { registerCollectionTypeConfigsCommands } from "../src/commands/collection-type-configs.js";
|
|
25
36
|
import { error } from "../src/lib/output.js";
|
|
37
|
+
import { installGlobalCrashHandlers } from "../src/lib/crash-handlers.js";
|
|
38
|
+
import { buildCliManifest } from "../src/lib/cli-manifest.js";
|
|
26
39
|
import { ApiError } from "../src/lib/api-client.js";
|
|
40
|
+
import { checkForUpdate } from "../src/lib/version-check.js";
|
|
41
|
+
import { loadCredentials } from "../src/lib/config.js";
|
|
42
|
+
import { checkSkillStatus } from "../src/lib/skill-installer.js";
|
|
43
|
+
import { registerSkillCommands } from "../src/commands/skill.js";
|
|
44
|
+
import { registerSecretsCommands } from "../src/commands/secrets.js";
|
|
45
|
+
import { registerBlobBucketsCommands } from "../src/commands/blob-buckets.js";
|
|
46
|
+
import { registerEnvCommands } from "../src/commands/env.js";
|
|
47
|
+
import { setCurrentEnvName, getCurrentEnvNameSafe, getProjectConfig, } from "../src/lib/env-resolver.js";
|
|
48
|
+
import { ProjectConfigError, PROJECT_CONFIG_DISPLAY_NAME, findProjectConfigPath } from "../src/lib/project-config.js";
|
|
49
|
+
import { maybeWarnLegacyMigration } from "../src/lib/migration-nag.js";
|
|
50
|
+
// Install global crash handlers first thing. Without these, an error that
|
|
51
|
+
// escapes the top-level `.catch` below — e.g. ERR_USE_AFTER_CLOSE thrown from
|
|
52
|
+
// inside an inquirer event callback when stdin isn't a usable TTY — makes Node
|
|
53
|
+
// print only its bare `Node.js vX` banner and die with no message (issue #972).
|
|
54
|
+
installGlobalCrashHandlers();
|
|
27
55
|
const __filename = fileURLToPath(import.meta.url);
|
|
28
56
|
const __dirname = dirname(__filename);
|
|
29
57
|
const pkg = JSON.parse(readFileSync(resolve(__dirname, "../../package.json"), "utf-8"));
|
|
58
|
+
/**
|
|
59
|
+
* Write a string to a file descriptor synchronously, in full, before
|
|
60
|
+
* returning. Handles two pitfalls of a single `fs.writeSync` on a pipe:
|
|
61
|
+
* - partial writes: `writeSync` may write fewer bytes than requested (e.g.
|
|
62
|
+
* only what fits in the OS pipe buffer), so we loop on the returned count;
|
|
63
|
+
* - `EAGAIN` on a non-blocking fd with a slow/full consumer: retry instead of
|
|
64
|
+
* throwing.
|
|
65
|
+
* This guarantees the whole payload reaches the kernel before the caller
|
|
66
|
+
* exits, with no dependency on the event loop draining stdout.
|
|
67
|
+
*/
|
|
68
|
+
function writeAllSync(fd, text) {
|
|
69
|
+
const buf = Buffer.from(text, "utf-8");
|
|
70
|
+
let offset = 0;
|
|
71
|
+
while (offset < buf.length) {
|
|
72
|
+
try {
|
|
73
|
+
offset += writeSync(fd, buf, offset, buf.length - offset);
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
// Retry on EAGAIN (non-blocking fd, consumer not ready yet). Rethrow
|
|
77
|
+
// anything else.
|
|
78
|
+
if (err.code === "EAGAIN")
|
|
79
|
+
continue;
|
|
80
|
+
throw err;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const isVersionFlag = process.argv.includes("--version") || process.argv.includes("-V");
|
|
85
|
+
const subcommand = process.argv[2];
|
|
86
|
+
const isJsonOutput = process.argv.includes("--json");
|
|
87
|
+
const skipHeader = isVersionFlag || isJsonOutput || subcommand === "login" || subcommand === "logout" || subcommand === "token";
|
|
88
|
+
// --env <name> is a global option that must be applied BEFORE any command
|
|
89
|
+
// code runs, because loadCredentials() and getServerUrl() both consult the
|
|
90
|
+
// active environment. Commander only parses options during .parse(), so we
|
|
91
|
+
// pre-extract it here via a tiny manual scan. The actual option is still
|
|
92
|
+
// registered on the program below so it appears in --help.
|
|
93
|
+
function preParseEnvFlag() {
|
|
94
|
+
const argv = process.argv;
|
|
95
|
+
for (let i = 2; i < argv.length; i++) {
|
|
96
|
+
const a = argv[i];
|
|
97
|
+
if (a === "--env" || a === "-e") {
|
|
98
|
+
return argv[i + 1] || null;
|
|
99
|
+
}
|
|
100
|
+
if (a.startsWith("--env="))
|
|
101
|
+
return a.slice("--env=".length);
|
|
102
|
+
}
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
const preParsedEnv = preParseEnvFlag();
|
|
106
|
+
if (preParsedEnv) {
|
|
107
|
+
setCurrentEnvName(preParsedEnv);
|
|
108
|
+
}
|
|
109
|
+
// Surface project-config errors loudly and early. A broken config file
|
|
110
|
+
// should fail fast with a clear message rather than producing cryptic
|
|
111
|
+
// "Not logged in" errors deeper in.
|
|
112
|
+
try {
|
|
113
|
+
getProjectConfig();
|
|
114
|
+
}
|
|
115
|
+
catch (err) {
|
|
116
|
+
if (err instanceof ProjectConfigError) {
|
|
117
|
+
error(err.message);
|
|
118
|
+
if (err.path)
|
|
119
|
+
error(` at ${err.path}`);
|
|
120
|
+
process.exit(1);
|
|
121
|
+
}
|
|
122
|
+
throw err;
|
|
123
|
+
}
|
|
124
|
+
// If the user explicitly passed --env <name>, verify it resolves to a
|
|
125
|
+
// real environment NOW so we don't silently fall back to legacy creds.
|
|
126
|
+
if (preParsedEnv) {
|
|
127
|
+
try {
|
|
128
|
+
const project = getProjectConfig();
|
|
129
|
+
if (!project) {
|
|
130
|
+
error(`--env was given but no ${PROJECT_CONFIG_DISPLAY_NAME} was found in this directory or any parent.`);
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
133
|
+
if (!project.environments[preParsedEnv]) {
|
|
134
|
+
const available = Object.keys(project.environments).join(", ") || "(none)";
|
|
135
|
+
error(`Environment "${preParsedEnv}" is not defined in ${PROJECT_CONFIG_DISPLAY_NAME}. Available: ${available}`);
|
|
136
|
+
process.exit(1);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
catch (err) {
|
|
140
|
+
if (err instanceof ProjectConfigError) {
|
|
141
|
+
error(err.message);
|
|
142
|
+
process.exit(1);
|
|
143
|
+
}
|
|
144
|
+
throw err;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (!skipHeader) {
|
|
148
|
+
const creds = loadCredentials();
|
|
149
|
+
// In project mode the env may declare an appId even before login.
|
|
150
|
+
// Show that so the header is useful during `env add` / `login` flows.
|
|
151
|
+
const envNameForHeader = getCurrentEnvNameSafe();
|
|
152
|
+
let headerAppId = creds?.currentAppId;
|
|
153
|
+
let headerAppName = creds?.currentAppName;
|
|
154
|
+
if (envNameForHeader && (!headerAppId || !headerAppName)) {
|
|
155
|
+
try {
|
|
156
|
+
const envCfg = getProjectConfig()?.environments[envNameForHeader];
|
|
157
|
+
headerAppId = headerAppId || envCfg?.appId;
|
|
158
|
+
headerAppName = headerAppName || envCfg?.appName;
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
// ignore
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
const appInfo = headerAppName
|
|
165
|
+
? `${headerAppName} (${headerAppId})`
|
|
166
|
+
: headerAppId || "None set";
|
|
167
|
+
// Prefer the active environment's apiUrl so the header stays correct
|
|
168
|
+
// even before you've logged in. Only fall back to credentials' serverUrl
|
|
169
|
+
// in legacy mode.
|
|
170
|
+
let server = creds?.serverUrl;
|
|
171
|
+
if (envNameForHeader) {
|
|
172
|
+
try {
|
|
173
|
+
const env = getProjectConfig()?.environments[envNameForHeader];
|
|
174
|
+
if (env)
|
|
175
|
+
server = env.apiUrl;
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
// fall through
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
server = server || "Not configured";
|
|
182
|
+
const projectConfigPath = findProjectConfigPath();
|
|
183
|
+
const envLabel = envNameForHeader
|
|
184
|
+
? ` | Env: ${envNameForHeader}`
|
|
185
|
+
: projectConfigPath
|
|
186
|
+
? " | Env: (none selected)"
|
|
187
|
+
: "";
|
|
188
|
+
console.log(chalk.dim(`CLI Version: ${pkg.version}${envLabel} | App: ${appInfo} | Server: ${server}`));
|
|
189
|
+
console.log();
|
|
190
|
+
}
|
|
191
|
+
// Nudge legacy-mode users (no .primitive/config.json, but a working
|
|
192
|
+
// `~/.primitive/credentials.json` + a sync marker in CWD) to migrate.
|
|
193
|
+
// Wholly self-contained: rate-limited to once per 24h, opt-out via
|
|
194
|
+
// PRIMITIVE_NO_MIGRATE_NAG=1, silent in CI / non-TTY. See #552.
|
|
195
|
+
maybeWarnLegacyMigration({ argv: process.argv });
|
|
30
196
|
const program = new Command();
|
|
31
197
|
program
|
|
32
198
|
.name("primitive")
|
|
199
|
+
.exitOverride()
|
|
33
200
|
.description(`CLI for administering Primitive applications.
|
|
34
201
|
|
|
35
202
|
Manage apps, users, integrations, prompts, workflows, and more from the command line.
|
|
36
203
|
Supports TOML-based configuration sync for version-controlled app settings.`)
|
|
37
204
|
.version(pkg.version)
|
|
205
|
+
// Global --env flag. Parsed above in preParseEnvFlag too; declaring it
|
|
206
|
+
// here so commander shows it in help output and forwards it to
|
|
207
|
+
// subcommands.
|
|
208
|
+
.option("-e, --env <name>", "Named environment from .primitive/config.json (e.g. dev, prod)")
|
|
38
209
|
.addHelpText("after", `
|
|
39
210
|
Examples:
|
|
40
|
-
$ primitive
|
|
41
|
-
$ primitive
|
|
42
|
-
$ primitive
|
|
43
|
-
$ primitive
|
|
44
|
-
$ primitive
|
|
211
|
+
$ primitive init my-app # Scaffold a new project + .primitive/config.json
|
|
212
|
+
$ primitive env list # Show environments
|
|
213
|
+
$ primitive env add prod --api-url ... # Add a new environment
|
|
214
|
+
$ primitive --env prod users list # Run a command against a specific env
|
|
215
|
+
$ primitive login # Authenticate via browser OAuth
|
|
216
|
+
$ primitive sync pull # Pull config (auto per-env path)
|
|
217
|
+
|
|
218
|
+
Project configuration (.primitive/config.json):
|
|
219
|
+
In a directory with a .primitive/config.json (or any subdirectory of one),
|
|
220
|
+
the CLI operates in project mode: credentials and sync state live under
|
|
221
|
+
<projectRoot>/.primitive/ and each named environment is isolated.
|
|
222
|
+
Without a .primitive/config.json the CLI runs in legacy mode, reading
|
|
223
|
+
credentials from ~/.primitive/credentials.json.
|
|
45
224
|
|
|
46
|
-
|
|
47
|
-
|
|
225
|
+
Environment selection (project mode):
|
|
226
|
+
1. --env <name> flag
|
|
227
|
+
2. PRIMITIVE_ENV env var
|
|
228
|
+
3. "defaultEnvironment" in .primitive/config.json
|
|
229
|
+
4. The only environment, if there's exactly one
|
|
48
230
|
|
|
49
231
|
App Context:
|
|
50
|
-
|
|
51
|
-
|
|
232
|
+
Bind an app to an environment via "appId" in .primitive/config.json, or
|
|
233
|
+
run 'primitive use <app>' to set one in credentials. Most commands accept
|
|
234
|
+
--app to override.
|
|
52
235
|
|
|
53
236
|
Documentation:
|
|
54
237
|
See https://primitive-labs.github.io/primitive-docs/ for full documentation.
|
|
55
238
|
`);
|
|
56
239
|
// Register all command groups (init first, then logically grouped)
|
|
57
240
|
registerInitCommand(program);
|
|
241
|
+
registerEnvCommands(program);
|
|
58
242
|
registerAuthCommands(program);
|
|
59
243
|
registerAppsCommands(program);
|
|
60
244
|
registerUsersCommands(program);
|
|
61
245
|
registerWaitlistCommands(program);
|
|
62
246
|
registerIntegrationsCommands(program);
|
|
247
|
+
registerWebhooksCommands(program);
|
|
248
|
+
registerCronTriggersCommands(program);
|
|
249
|
+
registerMetadataCommands(program);
|
|
63
250
|
registerPromptsCommands(program);
|
|
251
|
+
registerScriptsCommands(program);
|
|
64
252
|
registerWorkflowsCommands(program);
|
|
65
253
|
registerAdminsCommands(program);
|
|
66
254
|
registerCatalogCommands(program);
|
|
@@ -71,24 +259,88 @@ registerComparisonsCommands(program);
|
|
|
71
259
|
registerTokensCommands(program);
|
|
72
260
|
registerDatabasesCommands(program);
|
|
73
261
|
registerGroupsCommands(program);
|
|
262
|
+
registerRuleSetsCommands(program);
|
|
263
|
+
registerGroupTypeConfigsCommands(program);
|
|
264
|
+
registerDatabaseTypesCommands(program);
|
|
74
265
|
registerGuidesCommands(program);
|
|
75
266
|
registerDocumentsCommands(program);
|
|
267
|
+
registerEmailTemplatesCommands(program);
|
|
268
|
+
registerCollectionsCommands(program);
|
|
269
|
+
registerCollectionTypeConfigsCommands(program);
|
|
270
|
+
registerSkillCommands(program);
|
|
271
|
+
registerSecretsCommands(program);
|
|
272
|
+
registerBlobBucketsCommands(program);
|
|
273
|
+
// `primitive help --json` — machine-readable command manifest (issue #983).
|
|
274
|
+
//
|
|
275
|
+
// Mechanism: a pre-parse argv guard, NOT a registered `help` subcommand.
|
|
276
|
+
// Commander already injects its own built-in `help [command]`; registering our
|
|
277
|
+
// own would risk clobbering `primitive help <command>`. By special-casing the
|
|
278
|
+
// invocation here — after every register*Commands() call so the full tree is
|
|
279
|
+
// built — we serialize commander's public accessors and exit before parseAsync.
|
|
280
|
+
// It is auth-free, runs offline (no project config / credentials / network),
|
|
281
|
+
// and `--json` already suppresses the header, skill-status nag, and update
|
|
282
|
+
// check (see `isJsonOutput` above), so stdout carries pure JSON.
|
|
283
|
+
//
|
|
284
|
+
// Hidden/undocumented: there is no visible `help --json` entry in human help
|
|
285
|
+
// output; it's discoverable for tooling/agents that know to ask for it.
|
|
286
|
+
if (process.argv[2] === "help" && process.argv.includes("--json")) {
|
|
287
|
+
// Emit the full manifest synchronously, then exit. The naive form — the
|
|
288
|
+
// async `json()` helper (`console.log`) followed by an immediate
|
|
289
|
+
// `process.exit(0)` — races the flush on the documented `primitive help
|
|
290
|
+
// --json | jq` pipe path: the ~365KB manifest far exceeds the 64KB OS pipe
|
|
291
|
+
// buffer, so exiting before the kernel drains it truncates the JSON for
|
|
292
|
+
// piped consumers.
|
|
293
|
+
//
|
|
294
|
+
// Robust fix: write to fd 1 with `fs.writeSync` in a loop. A single
|
|
295
|
+
// `writeSync` is NOT enough — on a non-blocking pipe fd it returns after a
|
|
296
|
+
// *partial* write (only what currently fits in the pipe buffer) without
|
|
297
|
+
// looping, silently dropping the tail; and on a slow consumer it can throw
|
|
298
|
+
// `EAGAIN`. Looping over the returned byte count and retrying on `EAGAIN`
|
|
299
|
+
// guarantees every byte is handed to the kernel before `process.exit(0)`,
|
|
300
|
+
// independent of the event loop. Output is byte-identical to `json()`: the
|
|
301
|
+
// same `JSON.stringify(..., null, 2)` plus a single trailing newline.
|
|
302
|
+
writeAllSync(1, JSON.stringify(buildCliManifest(program, pkg.version), null, 2) + "\n");
|
|
303
|
+
process.exit(0);
|
|
304
|
+
}
|
|
76
305
|
// Global error handler
|
|
77
306
|
program.hook("preAction", () => {
|
|
78
307
|
// Reset API client state before each command
|
|
79
308
|
});
|
|
309
|
+
// Commands that should never show post-command messages (update check, skill status)
|
|
310
|
+
const suppressPostMessages = isJsonOutput || subcommand === "token";
|
|
311
|
+
async function runPostCommandMessages() {
|
|
312
|
+
if (suppressPostMessages)
|
|
313
|
+
return;
|
|
314
|
+
// Claude Code skill: auto-update if installed, or show hint if not.
|
|
315
|
+
// Skip for `init` (handles its own prompt), `skill` (explicit management),
|
|
316
|
+
// and non-interactive contexts (--json, --version, token, etc.).
|
|
317
|
+
if (!skipHeader && subcommand !== "skill" && subcommand !== "init") {
|
|
318
|
+
await checkSkillStatus();
|
|
319
|
+
}
|
|
320
|
+
await checkForUpdate(pkg.version);
|
|
321
|
+
}
|
|
80
322
|
// Parse and execute
|
|
81
|
-
program.parseAsync(process.argv)
|
|
323
|
+
program.parseAsync(process.argv)
|
|
324
|
+
.then(async () => {
|
|
325
|
+
await runPostCommandMessages();
|
|
326
|
+
})
|
|
327
|
+
.catch(async (err) => {
|
|
328
|
+
// Commander throws CommanderError for help display, version flag, etc.
|
|
329
|
+
// Run post-command messages then exit with the intended code.
|
|
330
|
+
if (err instanceof CommanderError) {
|
|
331
|
+
await runPostCommandMessages();
|
|
332
|
+
process.exit(err.exitCode);
|
|
333
|
+
}
|
|
82
334
|
if (err instanceof ApiError) {
|
|
83
335
|
error(err.message);
|
|
84
336
|
if (err.statusCode === 401) {
|
|
85
337
|
error("Try running 'primitive login' to authenticate.");
|
|
86
338
|
}
|
|
339
|
+
await runPostCommandMessages();
|
|
87
340
|
process.exit(err.statusCode === 401 ? 2 : 1);
|
|
88
341
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
342
|
+
error(err.message || "An unexpected error occurred");
|
|
343
|
+
await runPostCommandMessages();
|
|
344
|
+
process.exit(1);
|
|
93
345
|
});
|
|
94
346
|
//# sourceMappingURL=primitive.js.map
|