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.
Files changed (42) hide show
  1. {primethink_cli-1.3.3/primethink_cli.egg-info → primethink_cli-1.3.4}/PKG-INFO +6 -8
  2. {primethink_cli-1.3.3 → primethink_cli-1.3.4}/README.md +5 -7
  3. {primethink_cli-1.3.3 → primethink_cli-1.3.4}/SPECS.md +0 -1
  4. {primethink_cli-1.3.3 → primethink_cli-1.3.4}/USER_GUIDE.md +12 -4
  5. {primethink_cli-1.3.3 → primethink_cli-1.3.4}/docs/cli-reference.md +25 -21
  6. {primethink_cli-1.3.3 → primethink_cli-1.3.4}/primethink.py +59 -122
  7. {primethink_cli-1.3.3 → primethink_cli-1.3.4/primethink_cli.egg-info}/PKG-INFO +6 -8
  8. {primethink_cli-1.3.3 → primethink_cli-1.3.4}/pyproject.toml +1 -1
  9. {primethink_cli-1.3.3 → primethink_cli-1.3.4}/skills/primethink-cli/SKILL.md +4 -3
  10. {primethink_cli-1.3.3 → primethink_cli-1.3.4}/tests/test_cli.py +53 -1
  11. {primethink_cli-1.3.3 → primethink_cli-1.3.4}/tests/test_project_workflows.py +15 -134
  12. {primethink_cli-1.3.3 → primethink_cli-1.3.4}/DEVELOPER.md +0 -0
  13. {primethink_cli-1.3.3 → primethink_cli-1.3.4}/LICENSE +0 -0
  14. {primethink_cli-1.3.3 → primethink_cli-1.3.4}/MANIFEST.in +0 -0
  15. {primethink_cli-1.3.3 → primethink_cli-1.3.4}/docs/install.md +0 -0
  16. {primethink_cli-1.3.3 → primethink_cli-1.3.4}/docs/primethink_help_llms.txt +0 -0
  17. {primethink_cli-1.3.3 → primethink_cli-1.3.4}/docs/superpowers/specs/2026-08-16-install-attached-skills-design.md +0 -0
  18. {primethink_cli-1.3.3 → primethink_cli-1.3.4}/install/homebrew/primethink-cli.rb +0 -0
  19. {primethink_cli-1.3.3 → primethink_cli-1.3.4}/install/install.cmd +0 -0
  20. {primethink_cli-1.3.3 → primethink_cli-1.3.4}/install/install.ps1 +0 -0
  21. {primethink_cli-1.3.3 → primethink_cli-1.3.4}/install/install.sh +0 -0
  22. {primethink_cli-1.3.3 → primethink_cli-1.3.4}/primethink_cli.egg-info/SOURCES.txt +0 -0
  23. {primethink_cli-1.3.3 → primethink_cli-1.3.4}/primethink_cli.egg-info/dependency_links.txt +0 -0
  24. {primethink_cli-1.3.3 → primethink_cli-1.3.4}/primethink_cli.egg-info/entry_points.txt +0 -0
  25. {primethink_cli-1.3.3 → primethink_cli-1.3.4}/primethink_cli.egg-info/requires.txt +0 -0
  26. {primethink_cli-1.3.3 → primethink_cli-1.3.4}/primethink_cli.egg-info/top_level.txt +0 -0
  27. {primethink_cli-1.3.3 → primethink_cli-1.3.4}/primethink_mcp.py +0 -0
  28. {primethink_cli-1.3.3 → primethink_cli-1.3.4}/scripts/build.sh +0 -0
  29. {primethink_cli-1.3.3 → primethink_cli-1.3.4}/scripts/deploy.sh +0 -0
  30. {primethink_cli-1.3.3 → primethink_cli-1.3.4}/scripts/publish.sh +0 -0
  31. {primethink_cli-1.3.3 → primethink_cli-1.3.4}/setup.cfg +0 -0
  32. {primethink_cli-1.3.3 → primethink_cli-1.3.4}/setup.py +0 -0
  33. {primethink_cli-1.3.3 → primethink_cli-1.3.4}/skills/__init__.py +0 -0
  34. {primethink_cli-1.3.3 → primethink_cli-1.3.4}/tests/test_agent_commands.py +0 -0
  35. {primethink_cli-1.3.3 → primethink_cli-1.3.4}/tests/test_env_config.py +0 -0
  36. {primethink_cli-1.3.3 → primethink_cli-1.3.4}/tests/test_install_developer_skill.py +0 -0
  37. {primethink_cli-1.3.3 → primethink_cli-1.3.4}/tests/test_install_skill.py +0 -0
  38. {primethink_cli-1.3.3 → primethink_cli-1.3.4}/tests/test_live_app.py +0 -0
  39. {primethink_cli-1.3.3 → primethink_cli-1.3.4}/tests/test_mcp_server.py +0 -0
  40. {primethink_cli-1.3.3 → primethink_cli-1.3.4}/tests/test_search_commands.py +0 -0
  41. {primethink_cli-1.3.3 → primethink_cli-1.3.4}/tests/test_task_commands.py +0 -0
  42. {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
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. `test-ui` reuses an authenticated browser session, trusts only browser interaction/inspection tools by default, captures Kiro's final Markdown response, and writes the required report itself; `--trust-all-tools` is an explicit opt-in for reviewed guides that need broader access.
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. `test-ui` reuses an authenticated browser session, trusts only browser interaction/inspection tools by default, captures Kiro's final Markdown response, and writes the required report itself; `--trust-all-tools` is an explicit opt-in for reviewed guides that need broader access.
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. `test-ui` reuses an existing authenticated browser session, trusts only browser interaction/inspection tools by default, captures Kiro's final Markdown response, and writes the report itself. Use `--trust-all-tools` only when a reviewed testing guide genuinely requires broader access.
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` | `https://app.primethink.ai` | Web URL used for output/opening |
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
- ### `pt live-app test-ui`
353
-
354
- Launch `kiro-cli` in non-interactive agent mode to follow an agentic Playwright guide against one exact existing chat. It reads optional `SPECS.md`, adds any specific instructions, captures the agent's final Markdown response, and writes `test_results.md` under a timestamped `DIRECTORY/tests/` folder unless `--output-dir` is set.
355
-
356
- ```bash
357
- pt live-app test-ui DIRECTORY CHAT_ID [OPTIONS]
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` | `https://app.primethink.ai` | Web URL used for output/opening |
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
- @click.group()
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.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='https://app.primethink.ai', show_default=True, help='PrimeThink web application URL')
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='https://app.primethink.ai', show_default=True, help='PrimeThink web application URL')
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
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. `test-ui` reuses an authenticated browser session, trusts only browser interaction/inspection tools by default, captures Kiro's final Markdown response, and writes the required report itself; `--trust-all-tools` is an explicit opt-in for reviewed guides that need broader access.
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
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "primethink-cli"
7
- version = "1.3.3"
7
+ version = "1.3.4"
8
8
  description = "PrimeThink CLI - A powerful tool for interacting with PrimeThink AI API"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -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
- pt live-app test-ui DIRECTORY CHAT_ID [--guide FILE] [--instructions TEXT] [--timeout SECONDS]
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 | set-private TASK_ID # visibility; other types: task update --type
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.3\n"
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 primethink
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
- assert 'https://app.primethink.ai/chats/new-chat' in result.output
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', 'test-ui'):
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