taktcli 0.8.2__tar.gz → 0.8.3__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 (71) hide show
  1. {taktcli-0.8.2 → taktcli-0.8.3}/PKG-INFO +28 -2
  2. {taktcli-0.8.2 → taktcli-0.8.3}/README.md +27 -1
  3. {taktcli-0.8.2 → taktcli-0.8.3}/pyproject.toml +1 -1
  4. {taktcli-0.8.2 → taktcli-0.8.3}/taktcli/commands/wikis.py +32 -0
  5. {taktcli-0.8.2 → taktcli-0.8.3}/taktcli/skills/takt/references/cli.md +1 -1
  6. {taktcli-0.8.2 → taktcli-0.8.3}/taktcli/skills/takt/references/plumbing.md +3 -0
  7. {taktcli-0.8.2 → taktcli-0.8.3}/taktcli.egg-info/PKG-INFO +28 -2
  8. {taktcli-0.8.2 → taktcli-0.8.3}/tests/test_wikis.py +45 -1
  9. {taktcli-0.8.2 → taktcli-0.8.3}/setup.cfg +0 -0
  10. {taktcli-0.8.2 → taktcli-0.8.3}/taktcli/__init__.py +0 -0
  11. {taktcli-0.8.2 → taktcli-0.8.3}/taktcli/__main__.py +0 -0
  12. {taktcli-0.8.2 → taktcli-0.8.3}/taktcli/auth.py +0 -0
  13. {taktcli-0.8.2 → taktcli-0.8.3}/taktcli/commands/__init__.py +0 -0
  14. {taktcli-0.8.2 → taktcli-0.8.3}/taktcli/commands/_exec.py +0 -0
  15. {taktcli-0.8.2 → taktcli-0.8.3}/taktcli/commands/_run.py +0 -0
  16. {taktcli-0.8.2 → taktcli-0.8.3}/taktcli/commands/auth.py +0 -0
  17. {taktcli-0.8.2 → taktcli-0.8.3}/taktcli/commands/file.py +0 -0
  18. {taktcli-0.8.2 → taktcli-0.8.3}/taktcli/commands/gql.py +0 -0
  19. {taktcli-0.8.2 → taktcli-0.8.3}/taktcli/commands/hotpath.py +0 -0
  20. {taktcli-0.8.2 → taktcli-0.8.3}/taktcli/commands/label.py +0 -0
  21. {taktcli-0.8.2 → taktcli-0.8.3}/taktcli/commands/notif.py +0 -0
  22. {taktcli-0.8.2 → taktcli-0.8.3}/taktcli/commands/project.py +0 -0
  23. {taktcli-0.8.2 → taktcli-0.8.3}/taktcli/commands/schema.py +0 -0
  24. {taktcli-0.8.2 → taktcli-0.8.3}/taktcli/commands/skill.py +0 -0
  25. {taktcli-0.8.2 → taktcli-0.8.3}/taktcli/commands/task.py +0 -0
  26. {taktcli-0.8.2 → taktcli-0.8.3}/taktcli/commands/watch.py +0 -0
  27. {taktcli-0.8.2 → taktcli-0.8.3}/taktcli/commands/wiki.py +0 -0
  28. {taktcli-0.8.2 → taktcli-0.8.3}/taktcli/commands/workspace.py +0 -0
  29. {taktcli-0.8.2 → taktcli-0.8.3}/taktcli/credentials.py +0 -0
  30. {taktcli-0.8.2 → taktcli-0.8.3}/taktcli/errors.py +0 -0
  31. {taktcli-0.8.2 → taktcli-0.8.3}/taktcli/introspection.py +0 -0
  32. {taktcli-0.8.2 → taktcli-0.8.3}/taktcli/io.py +0 -0
  33. {taktcli-0.8.2 → taktcli-0.8.3}/taktcli/oauth.py +0 -0
  34. {taktcli-0.8.2 → taktcli-0.8.3}/taktcli/skills/explore-wiki/SKILL.md +0 -0
  35. {taktcli-0.8.2 → taktcli-0.8.3}/taktcli/skills/takt/SKILL.md +0 -0
  36. {taktcli-0.8.2 → taktcli-0.8.3}/taktcli/skills/takt/references/explore-wiki.md +0 -0
  37. {taktcli-0.8.2 → taktcli-0.8.3}/taktcli/skills/takt/references/grooming.md +0 -0
  38. {taktcli-0.8.2 → taktcli-0.8.3}/taktcli/skills/takt/references/tasks.md +0 -0
  39. {taktcli-0.8.2 → taktcli-0.8.3}/taktcli/skills/takt/references/wiki-writing.md +0 -0
  40. {taktcli-0.8.2 → taktcli-0.8.3}/taktcli/skills/takt/references/wiki.md +0 -0
  41. {taktcli-0.8.2 → taktcli-0.8.3}/taktcli/skills/takt/references/worker.md +0 -0
  42. {taktcli-0.8.2 → taktcli-0.8.3}/taktcli/skills/takt-cli/SKILL.md +0 -0
  43. {taktcli-0.8.2 → taktcli-0.8.3}/taktcli/skills/takt-worker/SKILL.md +0 -0
  44. {taktcli-0.8.2 → taktcli-0.8.3}/taktcli/transport.py +0 -0
  45. {taktcli-0.8.2 → taktcli-0.8.3}/taktcli/watch.py +0 -0
  46. {taktcli-0.8.2 → taktcli-0.8.3}/taktcli.egg-info/SOURCES.txt +0 -0
  47. {taktcli-0.8.2 → taktcli-0.8.3}/taktcli.egg-info/dependency_links.txt +0 -0
  48. {taktcli-0.8.2 → taktcli-0.8.3}/taktcli.egg-info/entry_points.txt +0 -0
  49. {taktcli-0.8.2 → taktcli-0.8.3}/taktcli.egg-info/requires.txt +0 -0
  50. {taktcli-0.8.2 → taktcli-0.8.3}/taktcli.egg-info/top_level.txt +0 -0
  51. {taktcli-0.8.2 → taktcli-0.8.3}/tests/test_auth.py +0 -0
  52. {taktcli-0.8.2 → taktcli-0.8.3}/tests/test_auth_commands.py +0 -0
  53. {taktcli-0.8.2 → taktcli-0.8.3}/tests/test_cli.py +0 -0
  54. {taktcli-0.8.2 → taktcli-0.8.3}/tests/test_credentials.py +0 -0
  55. {taktcli-0.8.2 → taktcli-0.8.3}/tests/test_file.py +0 -0
  56. {taktcli-0.8.2 → taktcli-0.8.3}/tests/test_gql.py +0 -0
  57. {taktcli-0.8.2 → taktcli-0.8.3}/tests/test_hotpath.py +0 -0
  58. {taktcli-0.8.2 → taktcli-0.8.3}/tests/test_introspection.py +0 -0
  59. {taktcli-0.8.2 → taktcli-0.8.3}/tests/test_io.py +0 -0
  60. {taktcli-0.8.2 → taktcli-0.8.3}/tests/test_label.py +0 -0
  61. {taktcli-0.8.2 → taktcli-0.8.3}/tests/test_notif.py +0 -0
  62. {taktcli-0.8.2 → taktcli-0.8.3}/tests/test_project.py +0 -0
  63. {taktcli-0.8.2 → taktcli-0.8.3}/tests/test_refresh_concurrency.py +0 -0
  64. {taktcli-0.8.2 → taktcli-0.8.3}/tests/test_schema.py +0 -0
  65. {taktcli-0.8.2 → taktcli-0.8.3}/tests/test_skill.py +0 -0
  66. {taktcli-0.8.2 → taktcli-0.8.3}/tests/test_task.py +0 -0
  67. {taktcli-0.8.2 → taktcli-0.8.3}/tests/test_transport.py +0 -0
  68. {taktcli-0.8.2 → taktcli-0.8.3}/tests/test_version.py +0 -0
  69. {taktcli-0.8.2 → taktcli-0.8.3}/tests/test_watch.py +0 -0
  70. {taktcli-0.8.2 → taktcli-0.8.3}/tests/test_wiki.py +0 -0
  71. {taktcli-0.8.2 → taktcli-0.8.3}/tests/test_workspace.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: taktcli
3
- Version: 0.8.2
3
+ Version: 0.8.3
4
4
  Summary: Authorized GraphQL CLI for Takt — full GraphQL surface from the shell, stdlib-only core
5
5
  Author: Takt
6
6
  License: MIT
@@ -230,6 +230,7 @@ Every MCP tool maps to a verb:
230
230
  | `create_wiki` | `wikis create` |
231
231
  | *(none — CLI-only; GraphQL `updateWiki` / `renameWiki`)* | `wikis update <slug>` |
232
232
  | *(none — CLI-only; GraphQL `wikiRenamePreview`)* | `wikis rename-preview <slug> --new-slug S` |
233
+ | *(none — CLI-only; GraphQL `deleteWiki` / `wikiDeletePreview`)* | `wikis delete <slug> --yes` / `--dry-run` |
233
234
  | `get_project_wiki` | `wikis get <project>` |
234
235
  | `request_upload_url_and_headers` + `confirm_upload` | `file upload <path>` (composite) |
235
236
  | `get_file_url` | `file url <id>` |
@@ -254,7 +255,7 @@ Notes:
254
255
  - The `wiki` page verbs take `--wiki SLUG` (default `$TAKT_WIKI`)
255
256
  and stream big markdown through `--content-file` / `--old-file` / `--new-file`
256
257
  (`-` = stdin).
257
- - The wiki-container verbs are `wikis list/create/get/update` (entity = **Wiki**). The
258
+ - The wiki-container verbs are `wikis list/create/get/update/rename-preview/delete` (entity = **Wiki**). The
258
259
  old `workspace list/create/get` verbs are a hard error naming the replacement
259
260
  for one release — *workspace* is being reclaimed to mean the tenant, and
260
261
  nothing else.
@@ -297,6 +298,31 @@ fails, the metadata edit does not run.
297
298
  `rename-preview` runs `wikiRenamePreview` and prints the same report without
298
299
  writing anything. It is gated like the rename.
299
300
 
301
+ ### Delete a Wiki container
302
+
303
+ ```bash
304
+ taktcli wikis delete knowledge --dry-run
305
+ taktcli wikis delete knowledge --yes
306
+ ```
307
+
308
+ Deleting is **permanent**: there is no trash and no undo. Without `--yes` the
309
+ command exits `3` before sending a request; `--dry-run` runs `wikiDeletePreview`
310
+ and prints the report without writing anything (with or without `--yes`). Both are
311
+ gated like a slug rename: a human workspace OWNER (`FORBIDDEN` otherwise, agents
312
+ included).
313
+
314
+ `deleteWiki` removes the wiki, its pages, their embeddings and its search feedback in
315
+ one transaction. Projects linked to it lose their wiki. Every `[[knowledge/…]]`
316
+ mention elsewhere in the workspace (task titles and descriptions, comments, result
317
+ labels and content, other wikis' pages, project/wiki/workspace descriptions)
318
+ becomes plain text `knowledge/… (deleted)`. A token whose wiki allowlist named it
319
+ loses that entry; a list left empty stays empty (confined to nothing). It prints
320
+ `{"deleteWiki":{"slug","pages","projects","tasks","comments","results",
321
+ "otherPages","links","unfixable"}}`: `pages` deleted, `projects` that lost their
322
+ wiki, rows marked per surface, `links` marked, and `unfixable` (app URLs to the
323
+ wiki, which now 404, and external config such as `WIKI_SLUG`/`TAKT_WIKI`, CLAUDE.md,
324
+ skills and scripts).
325
+
300
326
  ## Output & exit codes
301
327
 
302
328
  Default output is pretty JSON of the unwrapped `.data` payload. `--compact` =
@@ -214,6 +214,7 @@ Every MCP tool maps to a verb:
214
214
  | `create_wiki` | `wikis create` |
215
215
  | *(none — CLI-only; GraphQL `updateWiki` / `renameWiki`)* | `wikis update <slug>` |
216
216
  | *(none — CLI-only; GraphQL `wikiRenamePreview`)* | `wikis rename-preview <slug> --new-slug S` |
217
+ | *(none — CLI-only; GraphQL `deleteWiki` / `wikiDeletePreview`)* | `wikis delete <slug> --yes` / `--dry-run` |
217
218
  | `get_project_wiki` | `wikis get <project>` |
218
219
  | `request_upload_url_and_headers` + `confirm_upload` | `file upload <path>` (composite) |
219
220
  | `get_file_url` | `file url <id>` |
@@ -238,7 +239,7 @@ Notes:
238
239
  - The `wiki` page verbs take `--wiki SLUG` (default `$TAKT_WIKI`)
239
240
  and stream big markdown through `--content-file` / `--old-file` / `--new-file`
240
241
  (`-` = stdin).
241
- - The wiki-container verbs are `wikis list/create/get/update` (entity = **Wiki**). The
242
+ - The wiki-container verbs are `wikis list/create/get/update/rename-preview/delete` (entity = **Wiki**). The
242
243
  old `workspace list/create/get` verbs are a hard error naming the replacement
243
244
  for one release — *workspace* is being reclaimed to mean the tenant, and
244
245
  nothing else.
@@ -281,6 +282,31 @@ fails, the metadata edit does not run.
281
282
  `rename-preview` runs `wikiRenamePreview` and prints the same report without
282
283
  writing anything. It is gated like the rename.
283
284
 
285
+ ### Delete a Wiki container
286
+
287
+ ```bash
288
+ taktcli wikis delete knowledge --dry-run
289
+ taktcli wikis delete knowledge --yes
290
+ ```
291
+
292
+ Deleting is **permanent**: there is no trash and no undo. Without `--yes` the
293
+ command exits `3` before sending a request; `--dry-run` runs `wikiDeletePreview`
294
+ and prints the report without writing anything (with or without `--yes`). Both are
295
+ gated like a slug rename: a human workspace OWNER (`FORBIDDEN` otherwise, agents
296
+ included).
297
+
298
+ `deleteWiki` removes the wiki, its pages, their embeddings and its search feedback in
299
+ one transaction. Projects linked to it lose their wiki. Every `[[knowledge/…]]`
300
+ mention elsewhere in the workspace (task titles and descriptions, comments, result
301
+ labels and content, other wikis' pages, project/wiki/workspace descriptions)
302
+ becomes plain text `knowledge/… (deleted)`. A token whose wiki allowlist named it
303
+ loses that entry; a list left empty stays empty (confined to nothing). It prints
304
+ `{"deleteWiki":{"slug","pages","projects","tasks","comments","results",
305
+ "otherPages","links","unfixable"}}`: `pages` deleted, `projects` that lost their
306
+ wiki, rows marked per surface, `links` marked, and `unfixable` (app URLs to the
307
+ wiki, which now 404, and external config such as `WIKI_SLUG`/`TAKT_WIKI`, CLAUDE.md,
308
+ skills and scripts).
309
+
284
310
  ## Output & exit codes
285
311
 
286
312
  Default output is pretty JSON of the unwrapped `.data` payload. `--compact` =
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "taktcli"
3
- version = "0.8.2"
3
+ version = "0.8.3"
4
4
  description = "Authorized GraphQL CLI for Takt — full GraphQL surface from the shell, stdlib-only core"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -59,6 +59,19 @@ def register(subparsers) -> None:
59
59
  add_output_args(p)
60
60
  p.set_defaults(func=rename_preview)
61
61
 
62
+ p = verbs.add_parser(
63
+ "delete",
64
+ help="permanently delete a Wiki and its pages (workspace OWNER); marks refs to it as (deleted)",
65
+ )
66
+ p.add_argument("slug", help="Wiki slug (explicit; does not use TAKT_WIKI)")
67
+ p.add_argument("--yes", action="store_true", help="confirm the permanent delete")
68
+ p.add_argument(
69
+ "--dry-run", action="store_true",
70
+ help="print what the delete would remove and mark, without changing anything",
71
+ )
72
+ add_output_args(p)
73
+ p.set_defaults(func=delete)
74
+
62
75
  p = verbs.add_parser("get", help="get a project's Wiki")
63
76
  p.add_argument("project")
64
77
  add_output_args(p)
@@ -129,6 +142,25 @@ def rename_preview(args) -> int:
129
142
  return run_op(args, document, {"slug": args.slug, "newSlug": args.new_slug})
130
143
 
131
144
 
145
+ DELETE_REPORT_FIELDS = "slug pages projects tasks comments results otherPages links unfixable"
146
+
147
+
148
+ def delete(args) -> int:
149
+ """Permanently delete a Wiki via ``deleteWiki`` and print its report; ``--dry-run`` previews.
150
+
151
+ Refuses (exit 3) without ``--yes`` unless ``--dry-run``: there is no undo.
152
+ """
153
+ if args.dry_run:
154
+ document = f"query($slug:String!){{wikiDeletePreview(slug:$slug){{{DELETE_REPORT_FIELDS}}}}}"
155
+ return run_op(args, document, {"slug": args.slug})
156
+ if not args.yes:
157
+ raise ConfigError(
158
+ f"deleting wiki '{args.slug}' is permanent: pass --yes to confirm, or --dry-run to preview"
159
+ )
160
+ document = f"mutation($slug:String!){{deleteWiki(slug:$slug){{{DELETE_REPORT_FIELDS}}}}}"
161
+ return run_op(args, document, {"slug": args.slug})
162
+
163
+
132
164
  def get(args) -> int:
133
165
  document = (
134
166
  "query($slug:String!){"
@@ -122,5 +122,5 @@ Don't use: `claim` / `release` / `--lease` (deprecated aliases of `run` / `stop`
122
122
  | search results are ordered by date | you passed `--order-by UPDATED_AT`, or you're on `wiki list`/`wiki grep`. Check `order` in the output. |
123
123
  | does `run` lock the task? | no. Whoever dispatches decides who works it; check before starting a RUNNING task. |
124
124
  | cancel a task? | `taktcli task transition <id> cancel` |
125
- | a wiki container vs a page? | `wikis …` acts on whole wikis; `wiki …` on pages. `wikis update <slug> --name N` renames a wiki's display name; `--new-slug S --yes-rewrite-links` changes its slug and rewrites every link to it (OWNER only, `wikis rename-preview` first). |
125
+ | a wiki container vs a page? | `wikis …` acts on whole wikis; `wiki …` on pages. `wikis update <slug> --name N` renames a wiki's display name; `--new-slug S --yes-rewrite-links` changes its slug and rewrites every link to it (OWNER only, `wikis rename-preview` first). `wikis delete <slug> --yes` deletes it permanently and marks links to it `(deleted)` (OWNER only, `--dry-run` first). |
126
126
  | find mentions of a task | `wiki grep "web-42" --wiki <w>` (literal; never `--regex` on `[[…]]`) |
@@ -102,12 +102,15 @@ taktcli wikis get <project-slug>
102
102
  taktcli wikis update <slug> --name N | --description TEXT | --description-file d.md
103
103
  taktcli wikis rename-preview <slug> --new-slug S
104
104
  taktcli wikis update <slug> --new-slug S --yes-rewrite-links [--name N …]
105
+ taktcli wikis delete <slug> --dry-run
106
+ taktcli wikis delete <slug> --yes
105
107
  ```
106
108
 
107
109
  - `wikis create` makes a wiki; `wiki create` makes a page.
108
110
  - `wikis update` needs an explicit slug and at least one field; omitted fields stay.
109
111
  - Renaming a slug needs a human workspace OWNER, so an agent token gets `FORBIDDEN`. `--new-slug S` also needs `--yes-rewrite-links`. The rename rewrites every `[[old/…]]` mention and app URL to the wiki (`/wiki/old/…`, `/w/<ws>/wiki/old/…`) in page titles, content and summaries, task titles and descriptions, comments, result labels and content, and project, wiki and workspace descriptions of the workspace. It prints `renameWiki.report`: `pages`, `tasks`, `comments` and `results` (rows changed), `links` (occurrences rewritten) and `unfixable` (what you must update by hand, such as `WIKI_SLUG`/`TAKT_WIKI` env, CLAUDE.md, skills, scripts and bookmarks). The old slug 404s, and there is no redirect.
110
112
  - `wikis rename-preview` returns the same report without writing anything. Run it first, and update the external config it lists after the rename.
113
+ - `wikis delete` is **permanent** (no trash, no undo) and gated like a rename: a human workspace OWNER, so an agent token gets `FORBIDDEN`. It refuses without `--yes`; `--dry-run` prints the preview instead. The wiki's pages, their embeddings and its search feedback go with it; projects linked to it lose their wiki. Every `[[slug/…]]` mention elsewhere in the workspace (task titles and descriptions, comments, results, other wikis' pages, descriptions) becomes plain `slug/… (deleted)`. It prints the report: `pages` (deleted), `projects` (slugs that lost their wiki), `tasks`, `comments`, `results`, `otherPages` (rows marked), `links` (mentions marked) and `unfixable` (app URLs to the wiki, which now 404, and external config such as `WIKI_SLUG`/`TAKT_WIKI`, CLAUDE.md, skills and scripts). A token confined to that wiki keeps working only for what else it was confined to; confined to nothing, it reads nothing.
111
114
 
112
115
  ## Defaults and versions
113
116
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: taktcli
3
- Version: 0.8.2
3
+ Version: 0.8.3
4
4
  Summary: Authorized GraphQL CLI for Takt — full GraphQL surface from the shell, stdlib-only core
5
5
  Author: Takt
6
6
  License: MIT
@@ -230,6 +230,7 @@ Every MCP tool maps to a verb:
230
230
  | `create_wiki` | `wikis create` |
231
231
  | *(none — CLI-only; GraphQL `updateWiki` / `renameWiki`)* | `wikis update <slug>` |
232
232
  | *(none — CLI-only; GraphQL `wikiRenamePreview`)* | `wikis rename-preview <slug> --new-slug S` |
233
+ | *(none — CLI-only; GraphQL `deleteWiki` / `wikiDeletePreview`)* | `wikis delete <slug> --yes` / `--dry-run` |
233
234
  | `get_project_wiki` | `wikis get <project>` |
234
235
  | `request_upload_url_and_headers` + `confirm_upload` | `file upload <path>` (composite) |
235
236
  | `get_file_url` | `file url <id>` |
@@ -254,7 +255,7 @@ Notes:
254
255
  - The `wiki` page verbs take `--wiki SLUG` (default `$TAKT_WIKI`)
255
256
  and stream big markdown through `--content-file` / `--old-file` / `--new-file`
256
257
  (`-` = stdin).
257
- - The wiki-container verbs are `wikis list/create/get/update` (entity = **Wiki**). The
258
+ - The wiki-container verbs are `wikis list/create/get/update/rename-preview/delete` (entity = **Wiki**). The
258
259
  old `workspace list/create/get` verbs are a hard error naming the replacement
259
260
  for one release — *workspace* is being reclaimed to mean the tenant, and
260
261
  nothing else.
@@ -297,6 +298,31 @@ fails, the metadata edit does not run.
297
298
  `rename-preview` runs `wikiRenamePreview` and prints the same report without
298
299
  writing anything. It is gated like the rename.
299
300
 
301
+ ### Delete a Wiki container
302
+
303
+ ```bash
304
+ taktcli wikis delete knowledge --dry-run
305
+ taktcli wikis delete knowledge --yes
306
+ ```
307
+
308
+ Deleting is **permanent**: there is no trash and no undo. Without `--yes` the
309
+ command exits `3` before sending a request; `--dry-run` runs `wikiDeletePreview`
310
+ and prints the report without writing anything (with or without `--yes`). Both are
311
+ gated like a slug rename: a human workspace OWNER (`FORBIDDEN` otherwise, agents
312
+ included).
313
+
314
+ `deleteWiki` removes the wiki, its pages, their embeddings and its search feedback in
315
+ one transaction. Projects linked to it lose their wiki. Every `[[knowledge/…]]`
316
+ mention elsewhere in the workspace (task titles and descriptions, comments, result
317
+ labels and content, other wikis' pages, project/wiki/workspace descriptions)
318
+ becomes plain text `knowledge/… (deleted)`. A token whose wiki allowlist named it
319
+ loses that entry; a list left empty stays empty (confined to nothing). It prints
320
+ `{"deleteWiki":{"slug","pages","projects","tasks","comments","results",
321
+ "otherPages","links","unfixable"}}`: `pages` deleted, `projects` that lost their
322
+ wiki, rows marked per surface, `links` marked, and `unfixable` (app URLs to the
323
+ wiki, which now 404, and external config such as `WIKI_SLUG`/`TAKT_WIKI`, CLAUDE.md,
324
+ skills and scripts).
325
+
300
326
  ## Output & exit codes
301
327
 
302
328
  Default output is pretty JSON of the unwrapped `.data` payload. `--compact` =
@@ -169,7 +169,7 @@ def test_rename_preview_requires_new_slug(monkeypatch, capsys):
169
169
 
170
170
  @pytest.mark.parametrize("error_code,message", [
171
171
  ("DUPLICATE", "Wiki with slug 'renamed' already exists"),
172
- ("FORBIDDEN", "Workspace OWNER role required for wiki slug changes"),
172
+ ("FORBIDDEN", "Workspace OWNER role required to change a wiki's slug or delete it"),
173
173
  ])
174
174
  def test_update_server_errors_exit_one(monkeypatch, capsys, error_code, message):
175
175
  errors = [{"message": message, "extensions": {"code": error_code}}]
@@ -194,3 +194,47 @@ def test_update_output(monkeypatch, capsys, flags):
194
194
  assert not output.err
195
195
  if "--compact" in flags:
196
196
  assert len(output.out.splitlines()) == 1
197
+
198
+
199
+ DELETE_REPORT = {
200
+ "slug": "kb", "pages": 4, "projects": ["api"], "tasks": 2, "comments": 1, "results": 0,
201
+ "otherPages": 3, "links": 9, "unfixable": ["External config ..."],
202
+ }
203
+
204
+
205
+ def test_delete_requires_yes(monkeypatch, capsys):
206
+ capture = {}
207
+ patch(monkeypatch, capture=capture)
208
+ assert main(["wikis", "delete", "kb"]) == 3
209
+ err = capsys.readouterr().err
210
+ assert "permanent" in err and "--yes" in err and "--dry-run" in err
211
+ assert capture == {}
212
+
213
+
214
+ def test_delete_with_yes_calls_delete_wiki(monkeypatch, capsys):
215
+ code, cap = run(monkeypatch, ["wikis", "delete", "kb", "--yes"], data={"deleteWiki": DELETE_REPORT})
216
+ assert code == 0
217
+ assert cap["query"].startswith("mutation(")
218
+ assert "deleteWiki(slug:$slug){slug pages projects tasks comments results otherPages links unfixable}" in cap["query"]
219
+ assert cap["variables"] == {"slug": "kb"}
220
+ assert json.loads(capsys.readouterr().out) == {"deleteWiki": DELETE_REPORT}
221
+
222
+
223
+ @pytest.mark.parametrize("flags", [["--dry-run"], ["--dry-run", "--yes"]])
224
+ def test_delete_dry_run_only_previews(monkeypatch, capsys, flags):
225
+ code, cap = run(monkeypatch, ["wikis", "delete", "kb", *flags], data={"wikiDeletePreview": DELETE_REPORT})
226
+ assert code == 0
227
+ assert cap["query"].startswith("query(")
228
+ assert "wikiDeletePreview(slug:$slug){slug pages" in cap["query"]
229
+ assert "deleteWiki" not in cap["query"]
230
+ assert json.loads(capsys.readouterr().out) == {"wikiDeletePreview": DELETE_REPORT}
231
+
232
+
233
+ def test_delete_server_error_exits_one(monkeypatch, capsys):
234
+ errors = [{"message": "Workspace OWNER role required to change a wiki's slug or delete it",
235
+ "extensions": {"code": "FORBIDDEN"}}]
236
+ monkeypatch.setattr("taktcli.commands._run.resolve_auth", lambda: (URL, TOKEN))
237
+ opener = make_opener({"data": None, "errors": errors}, capture={})
238
+ monkeypatch.setattr("taktcli.commands._run.execute", partial(execute, opener=opener))
239
+ assert main(["wikis", "delete", "kb", "--yes"]) == 1
240
+ assert json.loads(capsys.readouterr().err) == {"errors": errors}
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