primethink-cli 1.3.3__tar.gz → 1.3.4__tar.gz
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.
- {primethink_cli-1.3.3/primethink_cli.egg-info → primethink_cli-1.3.4}/PKG-INFO +6 -8
- {primethink_cli-1.3.3 → primethink_cli-1.3.4}/README.md +5 -7
- {primethink_cli-1.3.3 → primethink_cli-1.3.4}/SPECS.md +0 -1
- {primethink_cli-1.3.3 → primethink_cli-1.3.4}/USER_GUIDE.md +12 -4
- {primethink_cli-1.3.3 → primethink_cli-1.3.4}/docs/cli-reference.md +25 -21
- {primethink_cli-1.3.3 → primethink_cli-1.3.4}/primethink.py +59 -122
- {primethink_cli-1.3.3 → primethink_cli-1.3.4/primethink_cli.egg-info}/PKG-INFO +6 -8
- {primethink_cli-1.3.3 → primethink_cli-1.3.4}/pyproject.toml +1 -1
- {primethink_cli-1.3.3 → primethink_cli-1.3.4}/skills/primethink-cli/SKILL.md +4 -3
- {primethink_cli-1.3.3 → primethink_cli-1.3.4}/tests/test_cli.py +53 -1
- {primethink_cli-1.3.3 → primethink_cli-1.3.4}/tests/test_project_workflows.py +15 -134
- {primethink_cli-1.3.3 → primethink_cli-1.3.4}/DEVELOPER.md +0 -0
- {primethink_cli-1.3.3 → primethink_cli-1.3.4}/LICENSE +0 -0
- {primethink_cli-1.3.3 → primethink_cli-1.3.4}/MANIFEST.in +0 -0
- {primethink_cli-1.3.3 → primethink_cli-1.3.4}/docs/install.md +0 -0
- {primethink_cli-1.3.3 → primethink_cli-1.3.4}/docs/primethink_help_llms.txt +0 -0
- {primethink_cli-1.3.3 → primethink_cli-1.3.4}/docs/superpowers/specs/2026-08-16-install-attached-skills-design.md +0 -0
- {primethink_cli-1.3.3 → primethink_cli-1.3.4}/install/homebrew/primethink-cli.rb +0 -0
- {primethink_cli-1.3.3 → primethink_cli-1.3.4}/install/install.cmd +0 -0
- {primethink_cli-1.3.3 → primethink_cli-1.3.4}/install/install.ps1 +0 -0
- {primethink_cli-1.3.3 → primethink_cli-1.3.4}/install/install.sh +0 -0
- {primethink_cli-1.3.3 → primethink_cli-1.3.4}/primethink_cli.egg-info/SOURCES.txt +0 -0
- {primethink_cli-1.3.3 → primethink_cli-1.3.4}/primethink_cli.egg-info/dependency_links.txt +0 -0
- {primethink_cli-1.3.3 → primethink_cli-1.3.4}/primethink_cli.egg-info/entry_points.txt +0 -0
- {primethink_cli-1.3.3 → primethink_cli-1.3.4}/primethink_cli.egg-info/requires.txt +0 -0
- {primethink_cli-1.3.3 → primethink_cli-1.3.4}/primethink_cli.egg-info/top_level.txt +0 -0
- {primethink_cli-1.3.3 → primethink_cli-1.3.4}/primethink_mcp.py +0 -0
- {primethink_cli-1.3.3 → primethink_cli-1.3.4}/scripts/build.sh +0 -0
- {primethink_cli-1.3.3 → primethink_cli-1.3.4}/scripts/deploy.sh +0 -0
- {primethink_cli-1.3.3 → primethink_cli-1.3.4}/scripts/publish.sh +0 -0
- {primethink_cli-1.3.3 → primethink_cli-1.3.4}/setup.cfg +0 -0
- {primethink_cli-1.3.3 → primethink_cli-1.3.4}/setup.py +0 -0
- {primethink_cli-1.3.3 → primethink_cli-1.3.4}/skills/__init__.py +0 -0
- {primethink_cli-1.3.3 → primethink_cli-1.3.4}/tests/test_agent_commands.py +0 -0
- {primethink_cli-1.3.3 → primethink_cli-1.3.4}/tests/test_env_config.py +0 -0
- {primethink_cli-1.3.3 → primethink_cli-1.3.4}/tests/test_install_developer_skill.py +0 -0
- {primethink_cli-1.3.3 → primethink_cli-1.3.4}/tests/test_install_skill.py +0 -0
- {primethink_cli-1.3.3 → primethink_cli-1.3.4}/tests/test_live_app.py +0 -0
- {primethink_cli-1.3.3 → primethink_cli-1.3.4}/tests/test_mcp_server.py +0 -0
- {primethink_cli-1.3.3 → primethink_cli-1.3.4}/tests/test_search_commands.py +0 -0
- {primethink_cli-1.3.3 → primethink_cli-1.3.4}/tests/test_task_commands.py +0 -0
- {primethink_cli-1.3.3 → primethink_cli-1.3.4}/tests/test_whoami.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: primethink-cli
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.4
|
|
4
4
|
Summary: PrimeThink CLI - A powerful tool for interacting with PrimeThink AI API
|
|
5
5
|
Author-email: PrimeThink <support@primethink.ai>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -73,7 +73,6 @@ pt chat send --agent AGENT_ID --message "your message"
|
|
|
73
73
|
| `pt live-app new` | Scaffold a React or HTML Live App from a GitHub template catalog |
|
|
74
74
|
| `pt live-app publish` | Create/update a Live App task and sync its flat artifact |
|
|
75
75
|
| `pt live-app test` | Sync a Live App into a new or existing chat |
|
|
76
|
-
| `pt live-app test-ui` | Run Kiro/Playwright tests against an existing Live App chat |
|
|
77
76
|
| `pt profile add` | Configure an API token for a profile |
|
|
78
77
|
| `pt profile use` | Switch the active profile |
|
|
79
78
|
| `pt profile list` | List configured profiles |
|
|
@@ -127,6 +126,8 @@ pt chat send --agent AGENT_ID --message "your message"
|
|
|
127
126
|
|
|
128
127
|
Every command that calls the API also accepts `--profile` (use a specific profile for one request) and `--api-url`/`-u` (override the API URL for one request). See the [full CLI reference](docs/cli-reference.md) for every option and example.
|
|
129
128
|
|
|
129
|
+
`--help` works in either position: `pt chat send --help`, `pt chat --help send`, and `pt --help chat send` all print the same page.
|
|
130
|
+
|
|
130
131
|
> **Tip:** `-p` is the short flag for `--profile` in the `pt task`, `pt agent`, and `pt search` groups, `pt image generate`, and `pt whoami`, but in the `pt chat` and `pt collection` groups there is no `-p` for profile — there `-p` means `--path` (a directory inside the chat/collection) on the file commands. Use the long form `--profile` when in doubt.
|
|
131
132
|
|
|
132
133
|
## Configuration
|
|
@@ -341,14 +342,11 @@ pt live-app test ./my-live-app --chat-id EXISTING_CHAT_ID
|
|
|
341
342
|
# Change any chat's renderer explicitly
|
|
342
343
|
pt chat type EXISTING_CHAT_ID live-app
|
|
343
344
|
pt chat type EXISTING_CHAT_ID chat
|
|
344
|
-
|
|
345
|
-
# Run the agentic Playwright guide against that exact chat
|
|
346
|
-
pt live-app test-ui ./my-live-app EXISTING_CHAT_ID \
|
|
347
|
-
--guide ./AGENTIC_UI_TESTING.md \
|
|
348
|
-
--instructions "Verify create, edit, and delete"
|
|
349
345
|
```
|
|
350
346
|
|
|
351
|
-
Project metadata follows the reference-script conventions: `.name.config`, `.description.config`, `GOAL.md`, and optional `INITIAL_PROMPT.md`; `.image.png` is uploaded when publishing a Live App. Existing same-named app documents receive a new `Production` version, while identical content is reported as unchanged.
|
|
347
|
+
Project metadata follows the reference-script conventions: `.name.config`, `.description.config`, `GOAL.md`, and optional `INITIAL_PROMPT.md`; `.image.png` is uploaded when publishing a Live App. Existing same-named app documents receive a new `Production` version, while identical content is reported as unchanged.
|
|
348
|
+
|
|
349
|
+
Automated Live App UI testing is no longer a `pt` command. It now lives in the `primethink-developer` skill as a deterministic, plan-driven workflow: the skill authors a reviewable `tests/test_plan.yaml`, runs it with a bundled Playwright runner (no LLM in the execution loop), reads the structured results, and heals failing selectors before re-running. Install it with `pt install-developer-skill`.
|
|
352
350
|
|
|
353
351
|
## MCP Server
|
|
354
352
|
|
|
@@ -39,7 +39,6 @@ pt chat send --agent AGENT_ID --message "your message"
|
|
|
39
39
|
| `pt live-app new` | Scaffold a React or HTML Live App from a GitHub template catalog |
|
|
40
40
|
| `pt live-app publish` | Create/update a Live App task and sync its flat artifact |
|
|
41
41
|
| `pt live-app test` | Sync a Live App into a new or existing chat |
|
|
42
|
-
| `pt live-app test-ui` | Run Kiro/Playwright tests against an existing Live App chat |
|
|
43
42
|
| `pt profile add` | Configure an API token for a profile |
|
|
44
43
|
| `pt profile use` | Switch the active profile |
|
|
45
44
|
| `pt profile list` | List configured profiles |
|
|
@@ -93,6 +92,8 @@ pt chat send --agent AGENT_ID --message "your message"
|
|
|
93
92
|
|
|
94
93
|
Every command that calls the API also accepts `--profile` (use a specific profile for one request) and `--api-url`/`-u` (override the API URL for one request). See the [full CLI reference](docs/cli-reference.md) for every option and example.
|
|
95
94
|
|
|
95
|
+
`--help` works in either position: `pt chat send --help`, `pt chat --help send`, and `pt --help chat send` all print the same page.
|
|
96
|
+
|
|
96
97
|
> **Tip:** `-p` is the short flag for `--profile` in the `pt task`, `pt agent`, and `pt search` groups, `pt image generate`, and `pt whoami`, but in the `pt chat` and `pt collection` groups there is no `-p` for profile — there `-p` means `--path` (a directory inside the chat/collection) on the file commands. Use the long form `--profile` when in doubt.
|
|
97
98
|
|
|
98
99
|
## Configuration
|
|
@@ -307,14 +308,11 @@ pt live-app test ./my-live-app --chat-id EXISTING_CHAT_ID
|
|
|
307
308
|
# Change any chat's renderer explicitly
|
|
308
309
|
pt chat type EXISTING_CHAT_ID live-app
|
|
309
310
|
pt chat type EXISTING_CHAT_ID chat
|
|
310
|
-
|
|
311
|
-
# Run the agentic Playwright guide against that exact chat
|
|
312
|
-
pt live-app test-ui ./my-live-app EXISTING_CHAT_ID \
|
|
313
|
-
--guide ./AGENTIC_UI_TESTING.md \
|
|
314
|
-
--instructions "Verify create, edit, and delete"
|
|
315
311
|
```
|
|
316
312
|
|
|
317
|
-
Project metadata follows the reference-script conventions: `.name.config`, `.description.config`, `GOAL.md`, and optional `INITIAL_PROMPT.md`; `.image.png` is uploaded when publishing a Live App. Existing same-named app documents receive a new `Production` version, while identical content is reported as unchanged.
|
|
313
|
+
Project metadata follows the reference-script conventions: `.name.config`, `.description.config`, `GOAL.md`, and optional `INITIAL_PROMPT.md`; `.image.png` is uploaded when publishing a Live App. Existing same-named app documents receive a new `Production` version, while identical content is reported as unchanged.
|
|
314
|
+
|
|
315
|
+
Automated Live App UI testing is no longer a `pt` command. It now lives in the `primethink-developer` skill as a deterministic, plan-driven workflow: the skill authors a reviewable `tests/test_plan.yaml`, runs it with a bundled Playwright runner (no LLM in the execution loop), reads the structured results, and heals failing selectors before re-running. Install it with `pt install-developer-skill`.
|
|
318
316
|
|
|
319
317
|
## MCP Server
|
|
320
318
|
|
|
@@ -69,7 +69,6 @@ To obtain an API key, go to `Settings > API Keys` in the PrimeThink app and gene
|
|
|
69
69
|
| `pt task test` | `POST /api/v1/chats` (new only), `PUT /api/v1/chats/{chat_id}/page_type` (existing only), `PUT /api/v1/chats/{chat_id}/goal` |
|
|
70
70
|
| `pt live-app publish` | task create/update + task version + task directory/document/version endpoints |
|
|
71
71
|
| `pt live-app test` | chat create/page-type/goal + chat directory/document/version endpoints |
|
|
72
|
-
| `pt live-app test-ui` | Local `kiro-cli` process; no PrimeThink API request by this command |
|
|
73
72
|
| `pt task export` | `GET /api/v1/tasks/{task_id}` (portable fields filtered client-side) |
|
|
74
73
|
| `pt task import` | `POST /api/v1/tasks/` |
|
|
75
74
|
| `pt task create-version` | `POST /api/v1/tasks/{task_id}/versions` |
|
|
@@ -810,12 +810,11 @@ pt live-app test ./my-live-app --chat-id CHAT_UUID --open
|
|
|
810
810
|
# Explicitly switch any existing chat's renderer
|
|
811
811
|
pt chat type CHAT_UUID live-app
|
|
812
812
|
pt chat type CHAT_UUID chat
|
|
813
|
-
|
|
814
|
-
# Agentic Playwright testing against that exact chat
|
|
815
|
-
pt live-app test-ui ./my-live-app CHAT_UUID --guide ./AGENTIC_UI_TESTING.md
|
|
816
813
|
```
|
|
817
814
|
|
|
818
|
-
The commands read `.name.config`, `.description.config`, and optional `GOAL.md`; publish also uploads `.image.png` when present. Same-named app documents are updated as new `Production` versions, preserving their IDs and relative links.
|
|
815
|
+
The commands read `.name.config`, `.description.config`, and optional `GOAL.md`; publish also uploads `.image.png` when present. Same-named app documents are updated as new `Production` versions, preserving their IDs and relative links.
|
|
816
|
+
|
|
817
|
+
Automated UI testing of a Live App is not part of the CLI. It is a deterministic, plan-driven workflow provided by the `primethink-developer` skill (`pt install-developer-skill`): the skill captures the running app's accessibility snapshot, authors a reviewable `tests/test_plan.yaml`, runs it with a bundled Playwright runner without an LLM in the execution loop, reads the structured results, and heals failing selectors before re-running.
|
|
819
818
|
|
|
820
819
|
## MCP Server
|
|
821
820
|
|
|
@@ -1366,6 +1365,15 @@ pt task create --help
|
|
|
1366
1365
|
pt chat sync-to --help
|
|
1367
1366
|
```
|
|
1368
1367
|
|
|
1368
|
+
`--help` also works *before* the command it describes — the CLI resolves the rest
|
|
1369
|
+
of the command path, so `pt --help chat send` and `pt chat --help send` print the
|
|
1370
|
+
same page as `pt chat send --help`:
|
|
1371
|
+
|
|
1372
|
+
```bash
|
|
1373
|
+
pt --help chat send
|
|
1374
|
+
pt chat --help send
|
|
1375
|
+
```
|
|
1376
|
+
|
|
1369
1377
|
### Documentation
|
|
1370
1378
|
|
|
1371
1379
|
- [README](README.md) - Quick start guide
|
|
@@ -17,7 +17,6 @@ Commands are organized into noun groups: `profile`, `live-app`, `chat`, `collect
|
|
|
17
17
|
- [`pt live-app new`](#pt-live-app-new)
|
|
18
18
|
- [`pt live-app publish`](#pt-live-app-publish)
|
|
19
19
|
- [`pt live-app test`](#pt-live-app-test)
|
|
20
|
-
- [`pt live-app test-ui`](#pt-live-app-test-ui)
|
|
21
20
|
- [Profiles: `pt profile`](#profiles-pt-profile)
|
|
22
21
|
- [`pt profile add`](#pt-profile-add)
|
|
23
22
|
- [`pt profile use`](#pt-profile-use)
|
|
@@ -100,6 +99,23 @@ Every command that calls the API accepts these two options:
|
|
|
100
99
|
> `--path` (a directory inside the chat/collection) on the file commands. When in doubt,
|
|
101
100
|
> use the long forms.
|
|
102
101
|
|
|
102
|
+
### Getting help
|
|
103
|
+
|
|
104
|
+
`--help` can be written before or after the command it describes — the CLI resolves
|
|
105
|
+
the whole command path that follows the flag, so these three are equivalent:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
pt chat send --help
|
|
109
|
+
pt chat --help send
|
|
110
|
+
pt --help chat send
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
A leading `--help` never errors: it descends as far as the path is recognized, so
|
|
114
|
+
`pt --help chat nope` describes `pt chat`, and `pt --help nope` describes `pt`. A
|
|
115
|
+
*trailing* `--help` keeps Click's own behavior, where the command path has to
|
|
116
|
+
resolve before the flag is reached — `pt chat nope --help` is still a
|
|
117
|
+
`No such command 'nope'` error.
|
|
118
|
+
|
|
103
119
|
### Output
|
|
104
120
|
|
|
105
121
|
- Commands that call the API print the JSON response, pretty-printed with 2-space indentation, to stdout. This makes output easy to pipe into `jq` or redirect to a file.
|
|
@@ -342,31 +358,19 @@ pt live-app test DIRECTORY [OPTIONS]
|
|
|
342
358
|
| `--app-dir DIRECTORY` | auto | Override flat artifact discovery |
|
|
343
359
|
| `--version-name NAME` | `Production` | Document version name |
|
|
344
360
|
| `--open` | off | Open the resulting chat in a browser |
|
|
345
|
-
| `--web-url URL` |
|
|
361
|
+
| `--web-url URL` | derived from API URL | Web URL used for output/opening (defaults to the active profile's API host, mapping `api.` → `app.`) |
|
|
346
362
|
|
|
347
363
|
```bash
|
|
348
364
|
pt live-app test ./decision-board
|
|
349
365
|
pt live-app test ./decision-board --chat-id CHAT_UUID --open
|
|
350
366
|
```
|
|
351
367
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
```
|
|
359
|
-
|
|
360
|
-
| Option | Default | Description |
|
|
361
|
-
|---|---|---|
|
|
362
|
-
| `--instructions TEXT`, `--test TEXT` | unset | Additional test focus |
|
|
363
|
-
| `--guide FILE` | discover `AGENTIC_UI_TESTING.md` | Testing guide |
|
|
364
|
-
| `--output-dir DIRECTORY` | `DIRECTORY/tests/TIMESTAMP` | Report directory |
|
|
365
|
-
| `--trust-all-tools` | off | Explicitly allow every configured Kiro tool instead of the restricted browser interaction/inspection allowlist |
|
|
366
|
-
| `--timeout SECONDS` | `1800` | Positive maximum runtime for the Kiro UI test process |
|
|
367
|
-
| `--web-url URL` | `https://app.primethink.ai` | PrimeThink web URL |
|
|
368
|
-
|
|
369
|
-
The command reuses an existing authenticated browser session and does not accept or place credentials in the Kiro prompt/process arguments. It fails if authentication blocks testing, Kiro exits nonzero, exceeds `--timeout`, or does not create the required `test_results.md`. Treat testing guides and project specs as untrusted content; use `--trust-all-tools` only after reviewing them and only when the restricted default cannot complete the test.
|
|
368
|
+
> **UI testing moved out of the CLI.** Automated Live App UI testing is now a
|
|
369
|
+
> deterministic, plan-driven workflow owned by the `primethink-developer` skill
|
|
370
|
+
> rather than a `pt` subcommand — the skill authors a reviewable
|
|
371
|
+
> `tests/test_plan.yaml`, runs it with a bundled Playwright runner (no LLM in the
|
|
372
|
+
> execution loop), and heals failing selectors. See the skill's UI-testing guide.
|
|
373
|
+
> This removes the CLI's `kiro-cli` dependency.
|
|
370
374
|
|
|
371
375
|
---
|
|
372
376
|
|
|
@@ -1125,7 +1129,7 @@ pt task test DIRECTORY [OPTIONS]
|
|
|
1125
1129
|
| `--workspace-id ID` | unset | Workspace for a newly created chat |
|
|
1126
1130
|
| `--temporary` / `--permanent` | temporary | Lifetime of a newly created chat |
|
|
1127
1131
|
| `--open` | off | Open the resulting chat in a browser |
|
|
1128
|
-
| `--web-url URL` |
|
|
1132
|
+
| `--web-url URL` | derived from API URL | Web URL used for output/opening (defaults to the active profile's API host, mapping `api.` → `app.`) |
|
|
1129
1133
|
|
|
1130
1134
|
```bash
|
|
1131
1135
|
pt task test ./tasks/morning-briefing
|
|
@@ -9,9 +9,7 @@ import tempfile
|
|
|
9
9
|
import zipfile
|
|
10
10
|
import contextlib
|
|
11
11
|
import mimetypes
|
|
12
|
-
import subprocess
|
|
13
12
|
import webbrowser
|
|
14
|
-
from datetime import datetime
|
|
15
13
|
from urllib.parse import quote, urlparse
|
|
16
14
|
|
|
17
15
|
import click
|
|
@@ -422,7 +420,47 @@ def _sync_bidirectional(list_url, api_url, token, path, local_dir, prefer, dry_r
|
|
|
422
420
|
f"{len(skipped)} skipped, {error_count} failed"
|
|
423
421
|
)
|
|
424
422
|
|
|
425
|
-
|
|
423
|
+
class _HelpDescendGroup(click.Group):
|
|
424
|
+
"""A group whose help flag may be written before the command it describes.
|
|
425
|
+
|
|
426
|
+
Click only honors ``--help`` after the full command path, so ``pt --help chat
|
|
427
|
+
send`` would print the top-level help and silently drop the rest. Here the
|
|
428
|
+
flag hops over the subcommand that follows it, making ``pt --help chat send``
|
|
429
|
+
and ``pt chat send --help`` equivalent. Subgroups inherit the behavior via
|
|
430
|
+
``group_class``, so each level of the path repeats the hop.
|
|
431
|
+
|
|
432
|
+
A leading help flag never turns into an error: the hop stops at the first
|
|
433
|
+
name this group does not know, and that group prints its own help. So
|
|
434
|
+
``pt --help chat nope`` describes ``pt chat``, just as ``pt --help nope``
|
|
435
|
+
describes ``pt``. A *trailing* flag keeps Click's semantics, where the
|
|
436
|
+
command path has to resolve before the flag is ever reached, so
|
|
437
|
+
``pt chat nope --help`` is still the usual "No such command" error.
|
|
438
|
+
"""
|
|
439
|
+
|
|
440
|
+
group_class = type
|
|
441
|
+
|
|
442
|
+
def parse_args(self, ctx, args):
|
|
443
|
+
return super().parse_args(ctx, self._descend_help(ctx, args))
|
|
444
|
+
|
|
445
|
+
def _descend_help(self, ctx, args):
|
|
446
|
+
"""Hop a leading help flag over the subcommand that follows it."""
|
|
447
|
+
help_names = set(ctx.help_option_names)
|
|
448
|
+
for index, arg in enumerate(args):
|
|
449
|
+
if arg in help_names:
|
|
450
|
+
rest = args[index + 1:]
|
|
451
|
+
# Hop one level only. If that subcommand is itself a group it
|
|
452
|
+
# repeats this, so the flag walks down as far as the path is
|
|
453
|
+
# known; anything else (an unknown name, a trailing flag,
|
|
454
|
+
# nothing at all) keeps this group's help.
|
|
455
|
+
if rest and self.get_command(ctx, rest[0]) is not None:
|
|
456
|
+
return args[:index] + [rest[0], arg] + rest[1:]
|
|
457
|
+
break
|
|
458
|
+
if not arg.startswith('-'):
|
|
459
|
+
break # The command path started; its own parser takes over.
|
|
460
|
+
return args
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
@click.group(cls=_HelpDescendGroup)
|
|
426
464
|
def cli():
|
|
427
465
|
"""PrimeThink CLI - A tool for interacting with PrimeThink API."""
|
|
428
466
|
pass
|
|
@@ -430,7 +468,7 @@ def cli():
|
|
|
430
468
|
@cli.command()
|
|
431
469
|
def version():
|
|
432
470
|
"""Display the version of PrimeThink CLI."""
|
|
433
|
-
click.echo("PrimeThink CLI v1.3.
|
|
471
|
+
click.echo("PrimeThink CLI v1.3.4")
|
|
434
472
|
|
|
435
473
|
|
|
436
474
|
@cli.command()
|
|
@@ -839,16 +877,6 @@ _LEGACY_APP_EXTENSIONS = {
|
|
|
839
877
|
}
|
|
840
878
|
|
|
841
879
|
|
|
842
|
-
_UI_TEST_TRUSTED_TOOLS = (
|
|
843
|
-
'browser_click', 'browser_console_messages', 'browser_drag',
|
|
844
|
-
'browser_fill_form', 'browser_find', 'browser_handle_dialog', 'browser_hover',
|
|
845
|
-
'browser_navigate', 'browser_navigate_back', 'browser_network_request',
|
|
846
|
-
'browser_network_requests', 'browser_press_key', 'browser_resize',
|
|
847
|
-
'browser_select_option', 'browser_snapshot', 'browser_tabs',
|
|
848
|
-
'browser_take_screenshot', 'browser_type', 'browser_wait_for',
|
|
849
|
-
)
|
|
850
|
-
|
|
851
|
-
|
|
852
880
|
def _read_optional_project_file(directory, filename, default=None):
|
|
853
881
|
path = Path(directory) / filename
|
|
854
882
|
if not path.is_file():
|
|
@@ -1162,6 +1190,19 @@ def _chat_web_url(chat_id, web_url):
|
|
|
1162
1190
|
return f"{web_url.rstrip('/')}/chats/{chat_id}"
|
|
1163
1191
|
|
|
1164
1192
|
|
|
1193
|
+
def _default_web_url(api_url):
|
|
1194
|
+
"""Derive the web app URL from the API URL so chat links honor the active
|
|
1195
|
+
profile. Maps an `api.` host to `app.` (https://api.primethink.ai ->
|
|
1196
|
+
https://app.primethink.ai); hosts without that prefix (custom/dev
|
|
1197
|
+
environments) are used as-is."""
|
|
1198
|
+
parsed = urlparse(api_url)
|
|
1199
|
+
scheme = parsed.scheme or 'https'
|
|
1200
|
+
host = parsed.netloc or parsed.path
|
|
1201
|
+
if host.startswith('api.'):
|
|
1202
|
+
host = 'app.' + host[len('api.'):]
|
|
1203
|
+
return f"{scheme}://{host}"
|
|
1204
|
+
|
|
1205
|
+
|
|
1165
1206
|
@live_app.command(name='publish')
|
|
1166
1207
|
@click.argument('directory', type=click.Path(exists=True, file_okay=False, path_type=Path))
|
|
1167
1208
|
@click.option('--task-id', default=None, type=int, help='Update this task instead of creating one')
|
|
@@ -1203,12 +1244,13 @@ def live_app_publish(directory, task_id, virtual_assistant_id, app_dir,
|
|
|
1203
1244
|
@click.option('--app-dir', default=None, type=click.Path(exists=True, file_okay=False, path_type=Path), help='Flat app artifact directory (auto-detects dist/, app/, or project root)')
|
|
1204
1245
|
@click.option('--version-name', default='Production', show_default=True, help='Document version name')
|
|
1205
1246
|
@click.option('--open', 'open_browser', is_flag=True, help='Open the chat in a browser after synchronization')
|
|
1206
|
-
@click.option('--web-url', default=
|
|
1247
|
+
@click.option('--web-url', default=None, help='PrimeThink web application URL (default: derived from the active profile API URL)')
|
|
1207
1248
|
@_connection_options
|
|
1208
1249
|
def live_app_test(directory, chat_id, workspace_id, temporary, app_dir,
|
|
1209
1250
|
version_name, open_browser, web_url, profile, api_url):
|
|
1210
1251
|
"""Synchronize a Live App and optional GOAL.md into a new or existing chat."""
|
|
1211
1252
|
api_url, token = _resolve_connection(profile, api_url)
|
|
1253
|
+
web_url = web_url or _default_web_url(api_url)
|
|
1212
1254
|
source_dir, files = _app_source_files(directory, app_dir)
|
|
1213
1255
|
goal = _read_project_goal(directory, required=False)
|
|
1214
1256
|
chat_id = _create_or_reuse_test_chat(
|
|
@@ -1225,112 +1267,6 @@ def live_app_test(directory, chat_id, workspace_id, temporary, app_dir,
|
|
|
1225
1267
|
webbrowser.open(url)
|
|
1226
1268
|
|
|
1227
1269
|
|
|
1228
|
-
@live_app.command(name='test-ui')
|
|
1229
|
-
@click.argument('directory', type=click.Path(exists=True, file_okay=False, path_type=Path))
|
|
1230
|
-
@click.argument('chat_id')
|
|
1231
|
-
@click.option('--instructions', '--test', default=None, help='Additional UI test instructions')
|
|
1232
|
-
@click.option('--guide', default=None, type=click.Path(exists=True, dir_okay=False, path_type=Path), help='Agentic UI testing guide (default: AGENTIC_UI_TESTING.md)')
|
|
1233
|
-
@click.option('--output-dir', default=None, type=click.Path(file_okay=False, path_type=Path), help='Report directory (default: DIRECTORY/tests/TIMESTAMP)')
|
|
1234
|
-
@click.option('--trust-all-tools', is_flag=True, help='Allow Kiro to use every configured tool instead of the restricted UI-test allowlist')
|
|
1235
|
-
@click.option('--timeout', default=1800, show_default=True, type=click.IntRange(min=1), help='Maximum UI test runtime in seconds')
|
|
1236
|
-
@click.option('--web-url', default='https://app.primethink.ai', show_default=True, help='PrimeThink web application URL')
|
|
1237
|
-
def live_app_test_ui(directory, chat_id, instructions, guide, output_dir,
|
|
1238
|
-
trust_all_tools, timeout, web_url):
|
|
1239
|
-
"""Run Kiro/Playwright UI testing against a Live App in an existing chat."""
|
|
1240
|
-
executable = shutil.which('kiro-cli')
|
|
1241
|
-
if executable is None:
|
|
1242
|
-
raise click.ClickException("kiro-cli is required for automated UI testing")
|
|
1243
|
-
|
|
1244
|
-
if guide is None:
|
|
1245
|
-
candidates = [
|
|
1246
|
-
Path.cwd() / 'AGENTIC_UI_TESTING.md',
|
|
1247
|
-
Path(directory) / 'AGENTIC_UI_TESTING.md',
|
|
1248
|
-
Path(directory).parent / 'AGENTIC_UI_TESTING.md',
|
|
1249
|
-
]
|
|
1250
|
-
guide = next((path for path in candidates if path.is_file()), None)
|
|
1251
|
-
if guide is None:
|
|
1252
|
-
raise click.ClickException(
|
|
1253
|
-
"AGENTIC_UI_TESTING.md not found; provide --guide PATH"
|
|
1254
|
-
)
|
|
1255
|
-
|
|
1256
|
-
timestamp = datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
|
|
1257
|
-
output_dir = Path(output_dir) if output_dir else Path(directory) / 'tests' / timestamp
|
|
1258
|
-
output_dir.mkdir(parents=True, exist_ok=True)
|
|
1259
|
-
report = output_dir / 'test_results.md'
|
|
1260
|
-
specs = _read_optional_project_file(directory, 'SPECS.md')
|
|
1261
|
-
app_name = (_read_optional_project_file(directory, '.name.config')
|
|
1262
|
-
or Path(directory).resolve().name).rstrip('\r\n')
|
|
1263
|
-
|
|
1264
|
-
context_lines = [
|
|
1265
|
-
f"- CHAT_URL: {_chat_web_url(chat_id, web_url)}",
|
|
1266
|
-
f"- CHAT_ID: {chat_id}",
|
|
1267
|
-
f"- APP_NAME: {app_name}",
|
|
1268
|
-
"- AUTH: Reuse the existing authenticated browser session. Never request, read, or expose login credentials; report an authentication blocker if the session is not logged in.",
|
|
1269
|
-
]
|
|
1270
|
-
|
|
1271
|
-
prompt = (
|
|
1272
|
-
"You are an automated UI tester. Use Playwright and follow the testing "
|
|
1273
|
-
"guide below to test the PrimeThink Live App in the specified existing chat.\n\n"
|
|
1274
|
-
f"## Testing Guide\n\n{Path(guide).read_text(encoding='utf-8')}\n\n"
|
|
1275
|
-
f"## Context\n\n{chr(10).join(context_lines)}"
|
|
1276
|
-
)
|
|
1277
|
-
if specs:
|
|
1278
|
-
prompt += f"\n\n## App Specifications (SPECS.md)\n\n{specs}"
|
|
1279
|
-
if instructions:
|
|
1280
|
-
prompt += f"\n\n## Specific Test Instructions\n\n{instructions}"
|
|
1281
|
-
prompt += (
|
|
1282
|
-
"\n\n## Output\n\nReturn the complete test report as your final Markdown response. "
|
|
1283
|
-
"Do not use filesystem tools to save it; the CLI captures your final output and writes it to "
|
|
1284
|
-
f"{report}. Follow the report format in the testing guide."
|
|
1285
|
-
)
|
|
1286
|
-
|
|
1287
|
-
click.echo(f"Testing Live App in chat {chat_id}")
|
|
1288
|
-
click.echo(f"Expected report: {report}")
|
|
1289
|
-
previous_report = None
|
|
1290
|
-
if report.is_file():
|
|
1291
|
-
stat = report.stat()
|
|
1292
|
-
previous_report = (stat.st_mtime_ns, stat.st_size)
|
|
1293
|
-
trust_argument = (
|
|
1294
|
-
'-a' if trust_all_tools
|
|
1295
|
-
else '--trust-tools=' + ','.join(_UI_TEST_TRUSTED_TOOLS)
|
|
1296
|
-
)
|
|
1297
|
-
child_env = os.environ.copy()
|
|
1298
|
-
child_env.pop('PRIMETHINK_UI_USERNAME', None)
|
|
1299
|
-
child_env.pop('PRIMETHINK_UI_PASSWORD', None)
|
|
1300
|
-
try:
|
|
1301
|
-
result = subprocess.run(
|
|
1302
|
-
[executable, 'chat', trust_argument, '--no-interactive', prompt],
|
|
1303
|
-
check=False, capture_output=True, text=True, env=child_env,
|
|
1304
|
-
timeout=timeout,
|
|
1305
|
-
)
|
|
1306
|
-
except subprocess.TimeoutExpired:
|
|
1307
|
-
raise click.ClickException(
|
|
1308
|
-
f"UI test timed out after {timeout} seconds"
|
|
1309
|
-
)
|
|
1310
|
-
if result.returncode != 0:
|
|
1311
|
-
raise click.ClickException(
|
|
1312
|
-
f"kiro-cli UI test exited with status {result.returncode}"
|
|
1313
|
-
)
|
|
1314
|
-
|
|
1315
|
-
captured_report = (result.stdout or '').strip()
|
|
1316
|
-
if captured_report:
|
|
1317
|
-
try:
|
|
1318
|
-
report.write_text(captured_report + '\n', encoding='utf-8')
|
|
1319
|
-
except OSError as e:
|
|
1320
|
-
raise click.ClickException(f"could not write UI test report {report}: {e}")
|
|
1321
|
-
elif report.is_file():
|
|
1322
|
-
stat = report.stat()
|
|
1323
|
-
current_report = (stat.st_mtime_ns, stat.st_size)
|
|
1324
|
-
if previous_report == current_report:
|
|
1325
|
-
raise click.ClickException(
|
|
1326
|
-
f"UI test ended without creating or updating the required report: {report}"
|
|
1327
|
-
)
|
|
1328
|
-
else:
|
|
1329
|
-
raise click.ClickException(
|
|
1330
|
-
f"UI test ended without creating or updating the required report: {report}"
|
|
1331
|
-
)
|
|
1332
|
-
click.echo(f"UI test report: {report}")
|
|
1333
|
-
|
|
1334
1270
|
def _skill_install_base(target, custom_dir):
|
|
1335
1271
|
"""Resolve the parent skills directory for an installation command."""
|
|
1336
1272
|
if custom_dir:
|
|
@@ -2334,13 +2270,14 @@ def task_publish(directory, task_id, virtual_assistant_id, profile, api_url):
|
|
|
2334
2270
|
@click.option('--workspace-id', default=None, help='Workspace for a newly created chat')
|
|
2335
2271
|
@click.option('--temporary/--permanent', default=True, show_default=True, help='Whether a newly created chat is temporary')
|
|
2336
2272
|
@click.option('--open', 'open_browser', is_flag=True, help='Open the chat in a browser after synchronization')
|
|
2337
|
-
@click.option('--web-url', default=
|
|
2273
|
+
@click.option('--web-url', default=None, help='PrimeThink web application URL (default: derived from the active profile API URL)')
|
|
2338
2274
|
@_connection_options
|
|
2339
2275
|
def task_test(directory, chat_id, workspace_id, temporary, open_browser,
|
|
2340
2276
|
web_url, profile, api_url):
|
|
2341
2277
|
"""Synchronize a task project's GOAL.md into a new or existing chat."""
|
|
2342
2278
|
goal = _read_project_goal(directory, required=True)
|
|
2343
2279
|
api_url, token = _resolve_connection(profile, api_url)
|
|
2280
|
+
web_url = web_url or _default_web_url(api_url)
|
|
2344
2281
|
chat_id = _create_or_reuse_test_chat(
|
|
2345
2282
|
chat_id, 'chat', workspace_id, temporary, api_url, token
|
|
2346
2283
|
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: primethink-cli
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.4
|
|
4
4
|
Summary: PrimeThink CLI - A powerful tool for interacting with PrimeThink AI API
|
|
5
5
|
Author-email: PrimeThink <support@primethink.ai>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -73,7 +73,6 @@ pt chat send --agent AGENT_ID --message "your message"
|
|
|
73
73
|
| `pt live-app new` | Scaffold a React or HTML Live App from a GitHub template catalog |
|
|
74
74
|
| `pt live-app publish` | Create/update a Live App task and sync its flat artifact |
|
|
75
75
|
| `pt live-app test` | Sync a Live App into a new or existing chat |
|
|
76
|
-
| `pt live-app test-ui` | Run Kiro/Playwright tests against an existing Live App chat |
|
|
77
76
|
| `pt profile add` | Configure an API token for a profile |
|
|
78
77
|
| `pt profile use` | Switch the active profile |
|
|
79
78
|
| `pt profile list` | List configured profiles |
|
|
@@ -127,6 +126,8 @@ pt chat send --agent AGENT_ID --message "your message"
|
|
|
127
126
|
|
|
128
127
|
Every command that calls the API also accepts `--profile` (use a specific profile for one request) and `--api-url`/`-u` (override the API URL for one request). See the [full CLI reference](docs/cli-reference.md) for every option and example.
|
|
129
128
|
|
|
129
|
+
`--help` works in either position: `pt chat send --help`, `pt chat --help send`, and `pt --help chat send` all print the same page.
|
|
130
|
+
|
|
130
131
|
> **Tip:** `-p` is the short flag for `--profile` in the `pt task`, `pt agent`, and `pt search` groups, `pt image generate`, and `pt whoami`, but in the `pt chat` and `pt collection` groups there is no `-p` for profile — there `-p` means `--path` (a directory inside the chat/collection) on the file commands. Use the long form `--profile` when in doubt.
|
|
131
132
|
|
|
132
133
|
## Configuration
|
|
@@ -341,14 +342,11 @@ pt live-app test ./my-live-app --chat-id EXISTING_CHAT_ID
|
|
|
341
342
|
# Change any chat's renderer explicitly
|
|
342
343
|
pt chat type EXISTING_CHAT_ID live-app
|
|
343
344
|
pt chat type EXISTING_CHAT_ID chat
|
|
344
|
-
|
|
345
|
-
# Run the agentic Playwright guide against that exact chat
|
|
346
|
-
pt live-app test-ui ./my-live-app EXISTING_CHAT_ID \
|
|
347
|
-
--guide ./AGENTIC_UI_TESTING.md \
|
|
348
|
-
--instructions "Verify create, edit, and delete"
|
|
349
345
|
```
|
|
350
346
|
|
|
351
|
-
Project metadata follows the reference-script conventions: `.name.config`, `.description.config`, `GOAL.md`, and optional `INITIAL_PROMPT.md`; `.image.png` is uploaded when publishing a Live App. Existing same-named app documents receive a new `Production` version, while identical content is reported as unchanged.
|
|
347
|
+
Project metadata follows the reference-script conventions: `.name.config`, `.description.config`, `GOAL.md`, and optional `INITIAL_PROMPT.md`; `.image.png` is uploaded when publishing a Live App. Existing same-named app documents receive a new `Production` version, while identical content is reported as unchanged.
|
|
348
|
+
|
|
349
|
+
Automated Live App UI testing is no longer a `pt` command. It now lives in the `primethink-developer` skill as a deterministic, plan-driven workflow: the skill authors a reviewable `tests/test_plan.yaml`, runs it with a bundled Playwright runner (no LLM in the execution loop), reads the structured results, and heals failing selectors before re-running. Install it with `pt install-developer-skill`.
|
|
352
350
|
|
|
353
351
|
## MCP Server
|
|
354
352
|
|
|
@@ -5,7 +5,7 @@ description: Use the PrimeThink CLI (`pt`) to interact with the PrimeThink API a
|
|
|
5
5
|
|
|
6
6
|
# PrimeThink CLI
|
|
7
7
|
|
|
8
|
-
The PrimeThink CLI is installed as `pt` (`pip install primethink-cli`; in the source repo, `pip install -e .` or run `python primethink.py …` directly). Full documentation: `pt --help`, `pt <group> --help`, or [docs/cli-reference.md](https://github.com/primethink-ai/primethink-cli/blob/main/docs/cli-reference.md).
|
|
8
|
+
The PrimeThink CLI is installed as `pt` (`pip install primethink-cli`; in the source repo, `pip install -e .` or run `python primethink.py …` directly). Full documentation: `pt --help`, `pt <group> --help`, or [docs/cli-reference.md](https://github.com/primethink-ai/primethink-cli/blob/main/docs/cli-reference.md). The help flag may come before or after the command path (`pt --help chat send` == `pt chat send --help`).
|
|
9
9
|
|
|
10
10
|
## Before you start
|
|
11
11
|
|
|
@@ -41,7 +41,7 @@ pt version | install-skill | install-developer-skill
|
|
|
41
41
|
pt live-app new DIRECTORY [--framework react|html] [--tailwind/--no-tailwind] [--flowbite/--no-flowbite] [--repo-url URL] [--ref REF]
|
|
42
42
|
pt live-app publish DIRECTORY --virtual-assistant-id ID [--task-id ID] [--app-dir DIR]
|
|
43
43
|
pt live-app test DIRECTORY [--chat-id CHAT_ID] [--app-dir DIR] [--open]
|
|
44
|
-
|
|
44
|
+
# Automated UI testing is not a CLI command — it lives in the primethink-developer skill (plan-driven, deterministic Playwright runner).
|
|
45
45
|
pt mcp # run PrimeThink as an MCP server over stdio (needs the [mcp] extra, Python 3.10+)
|
|
46
46
|
pt whoami [--profile NAME] # {"user": …, "groups": …} — verify which account a profile hits
|
|
47
47
|
pt profile add -t TOKEN [-p NAME] [-u URL] | use PROFILE | list | remove PROFILE
|
|
@@ -83,7 +83,8 @@ pt task update TASK_ID [fields…] # PATCH: only passed fields change;
|
|
|
83
83
|
pt task get TASK_ID
|
|
84
84
|
pt task delete TASK_ID [--yes] # DESTRUCTIVE; prompts unless --yes
|
|
85
85
|
pt task duplicate TASK_ID
|
|
86
|
-
pt task set-public TASK_ID
|
|
86
|
+
pt task set-public TASK_ID # visibility (was: task publish)
|
|
87
|
+
pt task set-private TASK_ID # visibility (was: task unpublish); other types: task update --type
|
|
87
88
|
pt task publish DIRECTORY --virtual-assistant-id ID [--task-id TASK_ID]
|
|
88
89
|
pt task test DIRECTORY [--chat-id CHAT_ID] [--temporary|--permanent] [--open]
|
|
89
90
|
pt task export TASK_ID [-o FILE] # portable config JSON (server fields stripped)
|
|
@@ -49,11 +49,63 @@ def test_cli_help(runner):
|
|
|
49
49
|
assert "Usage:" in result.output
|
|
50
50
|
assert "PrimeThink CLI - A tool for interacting with PrimeThink API." in result.output
|
|
51
51
|
|
|
52
|
+
def test_help_flag_before_command_shows_command_help(runner):
|
|
53
|
+
"""`pt --help chat` prints the chat group's help, not the top-level help."""
|
|
54
|
+
result = runner.invoke(cli, ['--help', 'chat'])
|
|
55
|
+
assert result.exit_code == 0
|
|
56
|
+
assert 'Usage: cli chat [OPTIONS] COMMAND [ARGS]...' in result.output
|
|
57
|
+
assert 'Chat management commands.' in result.output
|
|
58
|
+
|
|
59
|
+
def test_help_flag_before_nested_command_shows_that_command_help(runner):
|
|
60
|
+
"""A help flag in any position resolves the whole command path after it."""
|
|
61
|
+
before = runner.invoke(cli, ['--help', 'chat', 'send'])
|
|
62
|
+
middle = runner.invoke(cli, ['chat', '--help', 'send'])
|
|
63
|
+
after = runner.invoke(cli, ['chat', 'send', '--help'])
|
|
64
|
+
|
|
65
|
+
assert before.exit_code == middle.exit_code == after.exit_code == 0
|
|
66
|
+
assert before.output == middle.output == after.output
|
|
67
|
+
assert 'Usage: cli chat send [OPTIONS] [CHAT_ID_OR_MENTION]' in before.output
|
|
68
|
+
|
|
69
|
+
def test_help_flag_before_plain_command_shows_command_help(runner):
|
|
70
|
+
"""Descending also works for a leaf command directly under the root group."""
|
|
71
|
+
result = runner.invoke(cli, ['--help', 'version'])
|
|
72
|
+
assert result.exit_code == 0
|
|
73
|
+
assert 'Usage: cli version [OPTIONS]' in result.output
|
|
74
|
+
|
|
75
|
+
def test_help_flag_before_unknown_command_shows_group_help(runner):
|
|
76
|
+
"""An unrecognized name after the help flag falls back to the group's help."""
|
|
77
|
+
result = runner.invoke(cli, ['--help', 'not-a-command'])
|
|
78
|
+
assert result.exit_code == 0
|
|
79
|
+
assert 'Usage: cli [OPTIONS] COMMAND [ARGS]...' in result.output
|
|
80
|
+
assert 'PrimeThink CLI - A tool for interacting with PrimeThink API.' in result.output
|
|
81
|
+
|
|
82
|
+
def test_help_flag_before_unknown_subcommand_shows_enclosing_group_help(runner):
|
|
83
|
+
"""The descent stops at the first unknown name instead of erroring out."""
|
|
84
|
+
result = runner.invoke(cli, ['--help', 'chat', 'not-a-command'])
|
|
85
|
+
assert result.exit_code == 0
|
|
86
|
+
assert 'Usage: cli chat [OPTIONS] COMMAND [ARGS]...' in result.output
|
|
87
|
+
assert 'Chat management commands.' in result.output
|
|
88
|
+
|
|
89
|
+
def test_help_flag_after_unknown_subcommand_still_errors(runner):
|
|
90
|
+
"""A trailing flag keeps Click's semantics: the path must resolve first."""
|
|
91
|
+
result = runner.invoke(cli, ['chat', 'not-a-command', '--help'])
|
|
92
|
+
assert result.exit_code == 2
|
|
93
|
+
assert "No such command 'not-a-command'" in result.output
|
|
94
|
+
|
|
95
|
+
@patch('primethink.requests.request')
|
|
96
|
+
def test_help_flag_never_runs_the_command(mock_request, runner):
|
|
97
|
+
"""In either position the help flag short-circuits before any API call."""
|
|
98
|
+
for args in (['whoami', '--help'], ['--help', 'whoami']):
|
|
99
|
+
result = runner.invoke(cli, args)
|
|
100
|
+
assert result.exit_code == 0, args
|
|
101
|
+
assert 'Usage: cli whoami [OPTIONS]' in result.output, args
|
|
102
|
+
mock_request.assert_not_called()
|
|
103
|
+
|
|
52
104
|
def test_version_command(runner):
|
|
53
105
|
"""Test version command."""
|
|
54
106
|
result = runner.invoke(cli, ["version"])
|
|
55
107
|
assert result.exit_code == 0
|
|
56
|
-
assert result.output == "PrimeThink CLI v1.3.
|
|
108
|
+
assert result.output == "PrimeThink CLI v1.3.4\n"
|
|
57
109
|
|
|
58
110
|
@patch('primethink.CONFIG_DIR', Path('/tmp/.primethink'))
|
|
59
111
|
@patch('primethink.CONFIG_FILE', Path('/tmp/.primethink/config.json'))
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
"""Tests for project publishing and task/Live App chat-test workflows."""
|
|
2
2
|
|
|
3
3
|
from pathlib import Path
|
|
4
|
-
from types import SimpleNamespace
|
|
5
4
|
|
|
6
5
|
import pytest
|
|
7
6
|
|
|
8
|
-
import
|
|
9
|
-
from primethink import cli
|
|
7
|
+
from primethink import cli, _default_web_url
|
|
10
8
|
from tests.conftest import FakeResponse
|
|
11
9
|
|
|
12
10
|
|
|
@@ -178,7 +176,8 @@ def test_task_test_creates_chat_and_sets_goal(runner, api, tmp_path):
|
|
|
178
176
|
goal = api.call_args_list[1]
|
|
179
177
|
assert goal.args == ('PUT', 'https://api.test/api/v1/chats/new-chat/goal')
|
|
180
178
|
assert goal.kwargs['json'] == {'goal': 'Project goal\n'}
|
|
181
|
-
|
|
179
|
+
# Web URL is derived from the active profile's API host (api.test -> app.test).
|
|
180
|
+
assert 'https://app.test/chats/new-chat' in result.output
|
|
182
181
|
|
|
183
182
|
|
|
184
183
|
def test_live_app_test_reuses_chat_versions_existing_and_uploads_missing(
|
|
@@ -239,59 +238,6 @@ def test_same_document_response_is_successful_unchanged(runner, api, tmp_path):
|
|
|
239
238
|
assert '0 uploaded, 0 updated, 1 unchanged, 0 failed' in result.output
|
|
240
239
|
|
|
241
240
|
|
|
242
|
-
def test_live_app_test_ui_invokes_kiro_for_explicit_chat(
|
|
243
|
-
runner, monkeypatch, tmp_path):
|
|
244
|
-
project = _project(tmp_path, live_app=True)
|
|
245
|
-
(project / 'SPECS.md').write_text('Must load successfully.', encoding='utf-8')
|
|
246
|
-
guide = tmp_path / 'AGENTIC_UI_TESTING.md'
|
|
247
|
-
guide.write_text('Use Playwright and write a markdown report.', encoding='utf-8')
|
|
248
|
-
calls = []
|
|
249
|
-
|
|
250
|
-
monkeypatch.setattr(primethink.shutil, 'which', lambda name: '/usr/bin/kiro-cli')
|
|
251
|
-
|
|
252
|
-
output_dir = tmp_path / 'reports'
|
|
253
|
-
|
|
254
|
-
def run(argv, **kwargs):
|
|
255
|
-
calls.append((argv, kwargs))
|
|
256
|
-
return SimpleNamespace(returncode=0, stdout='# Passed report')
|
|
257
|
-
|
|
258
|
-
monkeypatch.setattr(primethink.subprocess, 'run', run)
|
|
259
|
-
monkeypatch.setenv('PRIMETHINK_UI_USERNAME', 'user@example.test')
|
|
260
|
-
monkeypatch.setenv('PRIMETHINK_UI_PASSWORD', 'top-secret-password')
|
|
261
|
-
|
|
262
|
-
result = runner.invoke(cli, [
|
|
263
|
-
'live-app', 'test-ui', str(project), 'chat-123',
|
|
264
|
-
'--guide', str(guide), '--output-dir', str(output_dir),
|
|
265
|
-
'--instructions', 'Verify the save button', '--timeout', '42',
|
|
266
|
-
])
|
|
267
|
-
|
|
268
|
-
assert result.exit_code == 0, result.output
|
|
269
|
-
assert len(calls) == 1
|
|
270
|
-
argv, kwargs = calls[0]
|
|
271
|
-
assert argv[:2] == ['/usr/bin/kiro-cli', 'chat']
|
|
272
|
-
assert argv[2].startswith('--trust-tools=')
|
|
273
|
-
assert 'browser_navigate' in argv[2]
|
|
274
|
-
assert argv[3] == '--no-interactive'
|
|
275
|
-
prompt = argv[4]
|
|
276
|
-
assert 'https://app.primethink.ai/chats/chat-123' in prompt
|
|
277
|
-
assert 'Must load successfully.' in prompt
|
|
278
|
-
assert 'Verify the save button' in prompt
|
|
279
|
-
assert str(output_dir / 'test_results.md') in prompt
|
|
280
|
-
assert 'top-secret-password' not in ' '.join(argv)
|
|
281
|
-
assert 'user@example.test' not in ' '.join(argv)
|
|
282
|
-
assert kwargs['check'] is False
|
|
283
|
-
assert kwargs['capture_output'] is True
|
|
284
|
-
assert kwargs['text'] is True
|
|
285
|
-
assert kwargs['timeout'] == 42
|
|
286
|
-
assert 'PRIMETHINK_UI_USERNAME' not in kwargs['env']
|
|
287
|
-
assert 'PRIMETHINK_UI_PASSWORD' not in kwargs['env']
|
|
288
|
-
assert 'read' not in argv[2].split('=', 1)[1].split(',')
|
|
289
|
-
assert 'write' not in argv[2].split('=', 1)[1].split(',')
|
|
290
|
-
assert 'browser_file_upload' not in argv[2]
|
|
291
|
-
assert (output_dir / 'test_results.md').read_text(encoding='utf-8') == '# Passed report\n'
|
|
292
|
-
assert 'UI test report:' in result.output
|
|
293
|
-
|
|
294
|
-
|
|
295
241
|
@pytest.mark.parametrize('existing_chat', [False, True])
|
|
296
242
|
@pytest.mark.parametrize('goal_content', [None, ' \n'])
|
|
297
243
|
def test_task_test_validates_goal_before_remote_mutation(
|
|
@@ -354,26 +300,6 @@ def test_existing_document_version_failure_does_not_upload_duplicate(
|
|
|
354
300
|
assert '1 failed' in result.output
|
|
355
301
|
|
|
356
302
|
|
|
357
|
-
def test_live_app_test_ui_fails_without_required_report(
|
|
358
|
-
runner, monkeypatch, tmp_path):
|
|
359
|
-
project = _project(tmp_path, live_app=True)
|
|
360
|
-
guide = tmp_path / 'AGENTIC_UI_TESTING.md'
|
|
361
|
-
guide.write_text('Write a report.', encoding='utf-8')
|
|
362
|
-
monkeypatch.setattr(primethink.shutil, 'which', lambda name: '/usr/bin/kiro-cli')
|
|
363
|
-
monkeypatch.setattr(
|
|
364
|
-
primethink.subprocess, 'run',
|
|
365
|
-
lambda argv, **kwargs: SimpleNamespace(returncode=0, stdout=''),
|
|
366
|
-
)
|
|
367
|
-
|
|
368
|
-
result = runner.invoke(cli, [
|
|
369
|
-
'live-app', 'test-ui', str(project), 'chat-123',
|
|
370
|
-
'--guide', str(guide), '--output-dir', str(tmp_path / 'missing-report'),
|
|
371
|
-
])
|
|
372
|
-
|
|
373
|
-
assert result.exit_code == 1
|
|
374
|
-
assert 'without creating or updating the required report' in result.output
|
|
375
|
-
|
|
376
|
-
|
|
377
303
|
def test_live_app_publish_allows_non_artifact_root_subdirectory(
|
|
378
304
|
runner, api, tmp_path):
|
|
379
305
|
project = tmp_path / 'root-app'
|
|
@@ -401,62 +327,6 @@ def test_live_app_publish_allows_non_artifact_root_subdirectory(
|
|
|
401
327
|
assert 'test_results.md' not in result.output
|
|
402
328
|
|
|
403
329
|
|
|
404
|
-
def test_live_app_test_ui_reports_timeout(runner, monkeypatch, tmp_path):
|
|
405
|
-
project = _project(tmp_path, live_app=True)
|
|
406
|
-
guide = tmp_path / 'AGENTIC_UI_TESTING.md'
|
|
407
|
-
guide.write_text('Write a report.', encoding='utf-8')
|
|
408
|
-
monkeypatch.setattr(primethink.shutil, 'which', lambda name: '/usr/bin/kiro-cli')
|
|
409
|
-
|
|
410
|
-
def run(argv, **kwargs):
|
|
411
|
-
raise primethink.subprocess.TimeoutExpired(argv, kwargs['timeout'])
|
|
412
|
-
|
|
413
|
-
monkeypatch.setattr(primethink.subprocess, 'run', run)
|
|
414
|
-
|
|
415
|
-
result = runner.invoke(cli, [
|
|
416
|
-
'live-app', 'test-ui', str(project), 'chat-123',
|
|
417
|
-
'--guide', str(guide), '--timeout', '1',
|
|
418
|
-
])
|
|
419
|
-
|
|
420
|
-
assert result.exit_code == 1
|
|
421
|
-
assert 'UI test timed out after 1 seconds' in result.output
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
def test_live_app_test_ui_rejects_non_positive_timeout(runner, tmp_path):
|
|
425
|
-
project = _project(tmp_path, live_app=True)
|
|
426
|
-
|
|
427
|
-
result = runner.invoke(cli, [
|
|
428
|
-
'live-app', 'test-ui', str(project), 'chat-123', '--timeout', '0',
|
|
429
|
-
])
|
|
430
|
-
|
|
431
|
-
assert result.exit_code == 2
|
|
432
|
-
assert 'Invalid value for \'--timeout\'' in result.output
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
def test_live_app_test_ui_rejects_unchanged_stale_report(
|
|
436
|
-
runner, monkeypatch, tmp_path):
|
|
437
|
-
project = _project(tmp_path, live_app=True)
|
|
438
|
-
guide = tmp_path / 'AGENTIC_UI_TESTING.md'
|
|
439
|
-
guide.write_text('Write a report.', encoding='utf-8')
|
|
440
|
-
output_dir = tmp_path / 'stale-report'
|
|
441
|
-
output_dir.mkdir()
|
|
442
|
-
report = output_dir / 'test_results.md'
|
|
443
|
-
report.write_text('# Old report', encoding='utf-8')
|
|
444
|
-
monkeypatch.setattr(primethink.shutil, 'which', lambda name: '/usr/bin/kiro-cli')
|
|
445
|
-
monkeypatch.setattr(
|
|
446
|
-
primethink.subprocess, 'run',
|
|
447
|
-
lambda argv, **kwargs: SimpleNamespace(returncode=0, stdout=''),
|
|
448
|
-
)
|
|
449
|
-
|
|
450
|
-
result = runner.invoke(cli, [
|
|
451
|
-
'live-app', 'test-ui', str(project), 'chat-123',
|
|
452
|
-
'--guide', str(guide), '--output-dir', str(output_dir),
|
|
453
|
-
])
|
|
454
|
-
|
|
455
|
-
assert result.exit_code == 1
|
|
456
|
-
assert 'without creating or updating the required report' in result.output
|
|
457
|
-
assert report.read_text(encoding='utf-8') == '# Old report'
|
|
458
|
-
|
|
459
|
-
|
|
460
330
|
def test_new_commands_are_visible_in_help(runner):
|
|
461
331
|
task_help = runner.invoke(cli, ['task', '--help'])
|
|
462
332
|
chat_help = runner.invoke(cli, ['chat', '--help'])
|
|
@@ -467,5 +337,16 @@ def test_new_commands_are_visible_in_help(runner):
|
|
|
467
337
|
assert name in task_help.output
|
|
468
338
|
assert 'unpublish' not in task_help.output
|
|
469
339
|
assert 'type' in chat_help.output
|
|
470
|
-
for name in ('publish', 'test'
|
|
340
|
+
for name in ('publish', 'test'):
|
|
471
341
|
assert name in app_help.output
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
@pytest.mark.parametrize('api_url,expected', [
|
|
345
|
+
('https://api.primethink.ai', 'https://app.primethink.ai'),
|
|
346
|
+
('https://api.primethink.ai/', 'https://app.primethink.ai'),
|
|
347
|
+
('https://app-dev.primethink.ai', 'https://app-dev.primethink.ai'),
|
|
348
|
+
('http://api.localhost:8000', 'http://app.localhost:8000'),
|
|
349
|
+
('https://staging.example.com', 'https://staging.example.com'),
|
|
350
|
+
])
|
|
351
|
+
def test_default_web_url_derives_from_api_host(api_url, expected):
|
|
352
|
+
assert _default_web_url(api_url) == expected
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|