@pplethai/cli 0.5.1 → 0.7.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.
package/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # pple CLI
2
2
 
3
- The Builder's terminal tool: three commands between a PPLE ID and a live app.
3
+ The Builder's terminal tool: three commands between a PPLE ID and a live app,
4
+ and everything else a Builder can do in the Console.
4
5
 
5
6
  ```bash
6
7
  pple auth login # sign in with your PPLE ID (browser, no password in the terminal)
@@ -9,8 +10,69 @@ pple init --name "Sign-up" # same, without a prompt (for agents/scripts)
9
10
  pple init brave-otter # set up an app that already exists on the platform
10
11
  pple deploy -m "Add the form" # send the changes and put them live
11
12
  pple upgrade # update pple itself from npm
13
+ pple help # every command; `pple <command> help` explains one
12
14
  ```
13
15
 
16
+ ## Builder Parity
17
+
18
+ Anything a Builder can view or change in the Console can be viewed or changed
19
+ here ([ADR-0020](../docs/adr/0020-builder-parity-between-console-and-cli.md)).
20
+ Platform Admin work (the approval queue, the all-apps index, direct edits,
21
+ Move owner, running a Code Review, admin Retire / Purge / Delete) stays in the
22
+ Console, and the CLI has no command for it.
23
+
24
+ | Area | Commands |
25
+ | ---- | -------- |
26
+ | Account | `auth login`, `auth logout`, `whoami`, `agreement` |
27
+ | Apps | `apps`, `init`, `info`, `rename`, `retire`, `retry-setup` |
28
+ | Deploys | `deploy`, `redeploy`, `deploys [<id>]` (reads a failed deploy's log) |
29
+ | Config | `env list`, `env set`, `env delete` |
30
+ | Publication | `promote`, `invites [add\|remove]`, `roles`, `request publish\|change\|limit\|retire`, `requests` |
31
+ | People | `collaborators [add\|remove]` |
32
+ | Notes and reviews | `notes [add]`, `reviews [<id>]` |
33
+
34
+ Every command that works on one app takes `--app <slug>` and otherwise uses
35
+ the App Link of the folder it runs in. Read commands take `--json` and print
36
+ the platform's own payload; Thai text is the default. `pple invites` and
37
+ `pple collaborators` print full phone numbers, as the Console does for an
38
+ Owner.
39
+
40
+ A CI test keeps this true. `test/builder-parity.test.ts` reads the Console's
41
+ `api` object (`console/web/console.ts`) and fails when a method that isn't
42
+ Platform Admin work has neither a command that names it in `covers`
43
+ (`src/registry.ts`) nor an entry with a reason in the test's `EXEMPT` list.
44
+ It proves a command *exists*, not that it behaves the same; permissions stay
45
+ aligned because only the Provisioner enforces them. CLI CI runs whenever
46
+ `console/web/console.ts` changes. A merged command reaches Builders only in a
47
+ CLI release (ADR-0008), so the published CLI can briefly lag the Console.
48
+
49
+ ## Asking for help is safe
50
+
51
+ `pple help`, `pple <command> help`, `pple <command> <subcommand> help`, `--help`
52
+ and `-h` explain a command and nothing else: they never reach the network,
53
+ change anything, ask a question, need a sign-in, or run the daily update check.
54
+ `help` counts only directly after the command's own words, so
55
+ `pple info --app help` still means an app with that slug and
56
+ `pple env set API_URL help` still sets the value `help`; `--help` and `-h`
57
+ win anywhere. Agents explore with `--help`, so this is checked for every
58
+ command in `test/help.test.ts`.
59
+
60
+ ## Consequential actions
61
+
62
+ At a terminal these ask a person, as the Console does. Run with nobody there
63
+ (an agent, a script, a pipe) each one refuses unless given the specific value
64
+ being confirmed, so the target is named in the Builder's transcript. There is
65
+ no generic `--yes`.
66
+
67
+ | Action | Unattended runs must pass |
68
+ | ------ | ------------------------- |
69
+ | `pple retire` | `--confirm <slug>` |
70
+ | `pple collaborators add <phone>` | the lookup's token, on a second run: `pple collaborators add --confirm <token>` |
71
+ | `pple request publish --address <name>` | `--confirm <name>` |
72
+ | `pple agreement` | nothing works: only a person at a terminal, or the Console, can accept |
73
+
74
+ The Skill tells agents to get the Builder's OK before passing any of these.
75
+
14
76
  Git is an implementation detail, and so is GitHub. A Builder is told what
15
77
  happened to *their app* — "Sending your changes…", "Volunteer Sign-up is live
16
78
  at https://brave-otter.ppleth.ai" — never what happened to a branch, and never
@@ -45,20 +107,55 @@ the same Provisioner `POST /apps` the Console uses, waits until the app is
45
107
  ready, then scaffolds it — or, given an App Slug, scaffolds an app that
46
108
  already exists. Either way it fetches the app's code, copies the golden-path
47
109
  template under the app's own name, writes `.pple/app.json` linking the folder
48
- to the app, and installs the platform Skill overlay the Builder's AI coding
49
- agent can load (ADR-0001, ADR-0006). The App Slug stays platform-generated;
110
+ to the app, and installs the Skill and Platform Rules the Builder's AI coding
111
+ agent reads (ADR-0001, ADR-0019). The App Slug stays platform-generated;
50
112
  bare `pple init` in a folder that already has an App Link is refused.
51
113
 
52
- **`pple deploy`** refreshes that same Skill overlay, records everything in
114
+ **`pple deploy`** records everything in
53
115
  the folder, sends it to the app's repo — merging in a Collaborator's work if
54
116
  there is any — then watches the platform build it and reports the live URL,
55
117
  or the build output that explains why nothing changed. It needs no
56
118
  arguments: the link file says which app. Account-only commands
57
- (`auth login` / `logout` / `whoami`) never touch Skills.
119
+ (`auth login` / `logout` / `whoami`) never touch the Skill or the Platform Rules.
120
+
121
+ **Skill and Platform Rules** are not in this package. `pple init`, `deploy`,
122
+ `info` and `env`, run in an app's folder, ask the platform for the current
123
+ hash of both and download one whole version only when it differs from the copy
124
+ on disk, so a merge to `main` reaches Builders on their next command with no CLI
125
+ release (ADR-0019). The copy is written once per agent, in the format it
126
+ reads, and every path is git-ignored:
127
+
128
+ | Agent | Skill | Platform Rules |
129
+ |---|---|---|
130
+ | Claude Code | `.claude/skills/pple-platform/` | `.claude/rules/pple-platform.md` |
131
+ | Cursor | `.agents/skills/pple-platform/` | `.cursor/rules/pple-platform.mdc` (`alwaysApply`) |
132
+ | Codex | `.agents/skills/pple-platform/` | `.pple/rules.md`, by the one-line pointer in `AGENTS.md` |
133
+ | Gemini CLI | `.agents/skills/pple-platform/` | none |
134
+
135
+ Which folder is refreshed is the one the command was run in, never the app
136
+ `--app` names; outside an app folder nothing happens, and asking for help never
137
+ refreshes. A refresh says something only when files changed (first install, a
138
+ location put back, a new version), and says so on stderr, so `--json` output
139
+ stays parseable. A refresh that fails warns, keeps the last copy and never
140
+ blocks the command; each location is replaced by renaming a finished copy over
141
+ the old one, so a download that dies halfway leaves nothing half-written.
142
+ Sibling skills, other rule files and an `AGENTS.md` the Builder wrote are never
143
+ touched: the pointer is created only when the file is missing. The version the
144
+ platform sends is untrusted — a file path that would leave its folder refuses
145
+ the whole version.
146
+
147
+ **`pple apps`, `rename`, `retire`, `retry-setup`, `redeploy`, `deploys`,
148
+ `promote`, `invites`, `request`, `requests`, `roles`, `collaborators`, `notes`,
149
+ `reviews`** are the Console's buttons from the terminal; each calls the same
150
+ Provisioner route the Console does. `request publish` and `request change`
151
+ upload an icon from a file path (`--icon`) the way the Console's picker does,
152
+ and name roles by key or Thai label, resolved against the Provisioner's
153
+ `GET /role-options` (`pple roles`) — the list the Console's picker shows.
58
154
 
59
155
  **`pple upgrade`** asks npm what the latest `@pplethai/cli` is and, when
60
156
  this copy is one npm's global root holds, installs it — which is also how
61
- Skills and the golden-path template move forward (ADR-0006, ADR-0007). Any
157
+ the golden-path template moves forward (ADR-0007; the Skill no longer
158
+ travels with the CLI, ADR-0019). Any
62
159
  other install channel is told the manual `npm install -g @pplethai/cli`
63
160
  line rather than having a second copy installed over the first, and so is a
64
161
  failed install, carrying npm's own reason. The CLI never uses `sudo`: a global
@@ -91,14 +188,19 @@ echo '{ "updateCheck": false }' > ~/.config/pple/config.json # this machine
91
188
 
92
189
  | File | What it holds |
93
190
  | ---- | ------------- |
94
- | `src/cli.ts` | argument parsing and the command table |
95
- | `src/commands/` | one file per command, each taking only `Deps` |
96
- | `src/deps.ts` | every seam: fetch, clock, subprocesses, output, template/skills dirs |
191
+ | `src/registry.ts` | the command table: every command, declared once with its help and what it covers; resolves a command line |
192
+ | `src/command.ts` | what a command is, and parsing its options |
193
+ | `src/help.ts` | the text of `pple help`, built from the table |
194
+ | `src/cli.ts` | runs one command line: help first, then the update check, then the command |
195
+ | `src/target.ts` | which app a command acts on (`--app`, else the App Link) |
196
+ | `src/commands/` | the commands, each taking `Deps` and what it was given |
197
+ | `src/labels.ts`, `src/requests.ts` | the Thai a Builder reads for platform values and Requests |
198
+ | `src/deps.ts` | every seam: fetch, clock, subprocesses, output, template dir |
97
199
  | `src/device-flow.ts` | RFC 8628 against PPLE ID |
98
200
  | `src/api.ts` | the Provisioner API, and the only place that knows its error codes |
99
201
  | `src/git.ts` | git, and the only place that names it or holds the repo token |
100
202
  | `src/scaffold.ts` | copying the golden-path template |
101
- | `src/skills.ts` | refreshing the platform Skill overlay into an app folder |
203
+ | `src/guidance.ts` | refreshing the Skill and Platform Rules into an app folder |
102
204
  | `src/npm.ts` | npm: the registry's latest version, and the install channel |
103
205
  | `src/update.ts` | the daily update check, its cadence state, and its opt-outs |
104
206
  | `askpass/pple-askpass.mjs` | what git runs to be told the app's repo token |
@@ -109,7 +211,7 @@ echo '{ "updateCheck": false }' > ~/.config/pple/config.json # this machine
109
211
  npm install
110
212
  npm test # drives whole commands against a fake Provisioner and real git
111
213
  npm run typecheck
112
- npm run build # bundles the template and Skills, then esbuild → dist/bin.js
214
+ npm run build # bundles the template, then esbuild → dist/bin.js
113
215
  ```
114
216
 
115
217
  Tests run the real command path — real `git` in a temp directory, a fake
@@ -122,11 +224,10 @@ minted. A local path would let every clone and push succeed with no credential
122
224
  at all, and nothing would show that the platform's token is what opens a
123
225
  private repo.
124
226
 
125
- The published package carries its own copies of the golden-path template
126
- (`npm run build:template` copies `../template`) and platform Skills
127
- (`npm run build:skills` copies `../skills/pple-platform`). Running from a
128
- checkout falls back to those repo paths directly. `PPLE_TEMPLATE_DIR`,
129
- `PPLE_SKILLS_DIR`, `PPLE_API_BASE`, `PPLE_SSO_BASE`, `PPLE_CLIENT_ID`,
227
+ The published package carries its own copy of the golden-path template
228
+ (`npm run build:template` copies `../template`). Running from a checkout falls
229
+ back to that repo path directly. The Skill is not bundled: tests serve it from
230
+ the fake platform. `PPLE_TEMPLATE_DIR`, `PPLE_API_BASE`, `PPLE_SSO_BASE`, `PPLE_CLIENT_ID`,
130
231
  `PPLE_REPO_BASE`, and `PPLE_REGISTRY_BASE` override the defaults for staging
131
232
  work.
132
233