taktcli 0.8.2__tar.gz → 0.8.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.
- {taktcli-0.8.2 → taktcli-0.8.4}/PKG-INFO +32 -5
- {taktcli-0.8.2 → taktcli-0.8.4}/README.md +31 -4
- {taktcli-0.8.2 → taktcli-0.8.4}/pyproject.toml +5 -1
- {taktcli-0.8.2 → taktcli-0.8.4}/taktcli/commands/wikis.py +32 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/taktcli/skills/takt/SKILL.md +31 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/taktcli/skills/takt/references/cli.md +12 -9
- {taktcli-0.8.2 → taktcli-0.8.4}/taktcli/skills/takt/references/plumbing.md +34 -9
- {taktcli-0.8.2 → taktcli-0.8.4}/taktcli/skills/takt/references/tasks.md +13 -10
- {taktcli-0.8.2 → taktcli-0.8.4}/taktcli/skills/takt/references/wiki.md +11 -8
- {taktcli-0.8.2 → taktcli-0.8.4}/taktcli/skills/takt/references/worker.md +17 -14
- taktcli-0.8.4/taktcli/skills/takt/scripts/markdown_index.py +148 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/taktcli.egg-info/PKG-INFO +32 -5
- {taktcli-0.8.2 → taktcli-0.8.4}/taktcli.egg-info/SOURCES.txt +5 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/tests/test_auth_commands.py +3 -3
- taktcli-0.8.4/tests/test_markdown_index.py +120 -0
- taktcli-0.8.4/tests/test_marketing_commands.py +41 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/tests/test_skill.py +12 -9
- taktcli-0.8.4/tests/test_skill_indexes.py +39 -0
- taktcli-0.8.4/tests/test_skill_wheel.py +31 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/tests/test_wikis.py +45 -1
- {taktcli-0.8.2 → taktcli-0.8.4}/setup.cfg +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/taktcli/__init__.py +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/taktcli/__main__.py +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/taktcli/auth.py +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/taktcli/commands/__init__.py +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/taktcli/commands/_exec.py +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/taktcli/commands/_run.py +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/taktcli/commands/auth.py +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/taktcli/commands/file.py +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/taktcli/commands/gql.py +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/taktcli/commands/hotpath.py +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/taktcli/commands/label.py +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/taktcli/commands/notif.py +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/taktcli/commands/project.py +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/taktcli/commands/schema.py +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/taktcli/commands/skill.py +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/taktcli/commands/task.py +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/taktcli/commands/watch.py +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/taktcli/commands/wiki.py +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/taktcli/commands/workspace.py +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/taktcli/credentials.py +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/taktcli/errors.py +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/taktcli/introspection.py +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/taktcli/io.py +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/taktcli/oauth.py +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/taktcli/skills/explore-wiki/SKILL.md +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/taktcli/skills/takt/references/explore-wiki.md +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/taktcli/skills/takt/references/grooming.md +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/taktcli/skills/takt/references/wiki-writing.md +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/taktcli/skills/takt-cli/SKILL.md +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/taktcli/skills/takt-worker/SKILL.md +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/taktcli/transport.py +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/taktcli/watch.py +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/taktcli.egg-info/dependency_links.txt +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/taktcli.egg-info/entry_points.txt +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/taktcli.egg-info/requires.txt +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/taktcli.egg-info/top_level.txt +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/tests/test_auth.py +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/tests/test_cli.py +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/tests/test_credentials.py +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/tests/test_file.py +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/tests/test_gql.py +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/tests/test_hotpath.py +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/tests/test_introspection.py +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/tests/test_io.py +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/tests/test_label.py +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/tests/test_notif.py +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/tests/test_project.py +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/tests/test_refresh_concurrency.py +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/tests/test_schema.py +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/tests/test_task.py +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/tests/test_transport.py +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/tests/test_version.py +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/tests/test_watch.py +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/tests/test_wiki.py +0 -0
- {taktcli-0.8.2 → taktcli-0.8.4}/tests/test_workspace.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: taktcli
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.4
|
|
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.
|
|
@@ -284,9 +285,10 @@ Successful metadata updates print `{"updateWiki":{"slug":…, "name":…,
|
|
|
284
285
|
`--yes-rewrite-links`; omitting confirmation exits `3` before sending a request.
|
|
285
286
|
Confirmation does not bypass the server's check that the caller is a human
|
|
286
287
|
workspace OWNER (`FORBIDDEN` otherwise). The rename rewrites every `[[old/…]]`
|
|
287
|
-
mention and app URL into the wiki (`https://takt.sh/wiki/old
|
|
288
|
-
`/w/<workspace>/wiki/old/…`, relative
|
|
289
|
-
|
|
288
|
+
mention and app URL into the wiki (`https://app.takt.sh/wiki/old/…` and legacy
|
|
289
|
+
`https://takt.sh/wiki/old/…` links, `/w/<workspace>/wiki/old/…`, relative
|
|
290
|
+
`/wiki/old/…`) across the workspace's pages, tasks, comments, results and
|
|
291
|
+
descriptions, in one transaction, and prints
|
|
290
292
|
`{"renameWiki":{"wiki":{…},"report":{"oldSlug","newSlug","pages","tasks",
|
|
291
293
|
"comments","results","links","unfixable"}}}`. `unfixable` lists what Takt cannot
|
|
292
294
|
reach (env such as `WIKI_SLUG`/`TAKT_WIKI`, CLAUDE.md, skills, scripts,
|
|
@@ -297,6 +299,31 @@ fails, the metadata edit does not run.
|
|
|
297
299
|
`rename-preview` runs `wikiRenamePreview` and prints the same report without
|
|
298
300
|
writing anything. It is gated like the rename.
|
|
299
301
|
|
|
302
|
+
### Delete a Wiki container
|
|
303
|
+
|
|
304
|
+
```bash
|
|
305
|
+
taktcli wikis delete knowledge --dry-run
|
|
306
|
+
taktcli wikis delete knowledge --yes
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
Deleting is **permanent**: there is no trash and no undo. Without `--yes` the
|
|
310
|
+
command exits `3` before sending a request; `--dry-run` runs `wikiDeletePreview`
|
|
311
|
+
and prints the report without writing anything (with or without `--yes`). Both are
|
|
312
|
+
gated like a slug rename: a human workspace OWNER (`FORBIDDEN` otherwise, agents
|
|
313
|
+
included).
|
|
314
|
+
|
|
315
|
+
`deleteWiki` removes the wiki, its pages, their embeddings and its search feedback in
|
|
316
|
+
one transaction. Projects linked to it lose their wiki. Every `[[knowledge/…]]`
|
|
317
|
+
mention elsewhere in the workspace (task titles and descriptions, comments, result
|
|
318
|
+
labels and content, other wikis' pages, project/wiki/workspace descriptions)
|
|
319
|
+
becomes plain text `knowledge/… (deleted)`. A token whose wiki allowlist named it
|
|
320
|
+
loses that entry; a list left empty stays empty (confined to nothing). It prints
|
|
321
|
+
`{"deleteWiki":{"slug","pages","projects","tasks","comments","results",
|
|
322
|
+
"otherPages","links","unfixable"}}`: `pages` deleted, `projects` that lost their
|
|
323
|
+
wiki, rows marked per surface, `links` marked, and `unfixable` (app URLs to the
|
|
324
|
+
wiki, which now 404, and external config such as `WIKI_SLUG`/`TAKT_WIKI`, CLAUDE.md,
|
|
325
|
+
skills and scripts).
|
|
326
|
+
|
|
300
327
|
## Output & exit codes
|
|
301
328
|
|
|
302
329
|
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.
|
|
@@ -268,9 +269,10 @@ Successful metadata updates print `{"updateWiki":{"slug":…, "name":…,
|
|
|
268
269
|
`--yes-rewrite-links`; omitting confirmation exits `3` before sending a request.
|
|
269
270
|
Confirmation does not bypass the server's check that the caller is a human
|
|
270
271
|
workspace OWNER (`FORBIDDEN` otherwise). The rename rewrites every `[[old/…]]`
|
|
271
|
-
mention and app URL into the wiki (`https://takt.sh/wiki/old
|
|
272
|
-
`/w/<workspace>/wiki/old/…`, relative
|
|
273
|
-
|
|
272
|
+
mention and app URL into the wiki (`https://app.takt.sh/wiki/old/…` and legacy
|
|
273
|
+
`https://takt.sh/wiki/old/…` links, `/w/<workspace>/wiki/old/…`, relative
|
|
274
|
+
`/wiki/old/…`) across the workspace's pages, tasks, comments, results and
|
|
275
|
+
descriptions, in one transaction, and prints
|
|
274
276
|
`{"renameWiki":{"wiki":{…},"report":{"oldSlug","newSlug","pages","tasks",
|
|
275
277
|
"comments","results","links","unfixable"}}}`. `unfixable` lists what Takt cannot
|
|
276
278
|
reach (env such as `WIKI_SLUG`/`TAKT_WIKI`, CLAUDE.md, skills, scripts,
|
|
@@ -281,6 +283,31 @@ fails, the metadata edit does not run.
|
|
|
281
283
|
`rename-preview` runs `wikiRenamePreview` and prints the same report without
|
|
282
284
|
writing anything. It is gated like the rename.
|
|
283
285
|
|
|
286
|
+
### Delete a Wiki container
|
|
287
|
+
|
|
288
|
+
```bash
|
|
289
|
+
taktcli wikis delete knowledge --dry-run
|
|
290
|
+
taktcli wikis delete knowledge --yes
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
Deleting is **permanent**: there is no trash and no undo. Without `--yes` the
|
|
294
|
+
command exits `3` before sending a request; `--dry-run` runs `wikiDeletePreview`
|
|
295
|
+
and prints the report without writing anything (with or without `--yes`). Both are
|
|
296
|
+
gated like a slug rename: a human workspace OWNER (`FORBIDDEN` otherwise, agents
|
|
297
|
+
included).
|
|
298
|
+
|
|
299
|
+
`deleteWiki` removes the wiki, its pages, their embeddings and its search feedback in
|
|
300
|
+
one transaction. Projects linked to it lose their wiki. Every `[[knowledge/…]]`
|
|
301
|
+
mention elsewhere in the workspace (task titles and descriptions, comments, result
|
|
302
|
+
labels and content, other wikis' pages, project/wiki/workspace descriptions)
|
|
303
|
+
becomes plain text `knowledge/… (deleted)`. A token whose wiki allowlist named it
|
|
304
|
+
loses that entry; a list left empty stays empty (confined to nothing). It prints
|
|
305
|
+
`{"deleteWiki":{"slug","pages","projects","tasks","comments","results",
|
|
306
|
+
"otherPages","links","unfixable"}}`: `pages` deleted, `projects` that lost their
|
|
307
|
+
wiki, rows marked per surface, `links` marked, and `unfixable` (app URLs to the
|
|
308
|
+
wiki, which now 404, and external config such as `WIKI_SLUG`/`TAKT_WIKI`, CLAUDE.md,
|
|
309
|
+
skills and scripts).
|
|
310
|
+
|
|
284
311
|
## Output & exit codes
|
|
285
312
|
|
|
286
313
|
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.
|
|
3
|
+
version = "0.8.4"
|
|
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"
|
|
@@ -25,10 +25,14 @@ testpaths = ["tests"]
|
|
|
25
25
|
|
|
26
26
|
[tool.setuptools.packages.find]
|
|
27
27
|
include = ["taktcli*"]
|
|
28
|
+
exclude = ["*.__pycache__"]
|
|
28
29
|
|
|
29
30
|
[tool.setuptools.package-data]
|
|
30
31
|
taktcli = ["skills/**/*"]
|
|
31
32
|
|
|
33
|
+
[tool.setuptools.exclude-package-data]
|
|
34
|
+
"*" = ["**/__pycache__/*", "*.py[cod]", "**/*.py[cod]"]
|
|
35
|
+
|
|
32
36
|
[build-system]
|
|
33
37
|
requires = ["setuptools>=75"]
|
|
34
38
|
build-backend = "setuptools.build_meta"
|
|
@@ -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!){"
|
|
@@ -15,6 +15,29 @@ description: >
|
|
|
15
15
|
|
|
16
16
|
Takt is a task board plus a wiki. `taktcli` is its CLI: argv in, GraphQL out, JSON on stdout.
|
|
17
17
|
|
|
18
|
+
## Index
|
|
19
|
+
|
|
20
|
+
Read a chapter with `sed -n 'A,Bp'` on this file. Long references have their own indexes.
|
|
21
|
+
|
|
22
|
+
- [Quick start](#quick-start): lines 34–40. Check identity, choose a mode and read current state before acting.
|
|
23
|
+
- [Modes](#modes): lines 41–53. Choose CLI lookup, wiki exploration or the worker loop and its detailed reference.
|
|
24
|
+
- [Setup](#setup): lines 54–62. Resolve CLI version, credentials, project and wiki.
|
|
25
|
+
- [Rules](#rules): lines 63–71. Check shell conventions, exit codes and task ownership before writes.
|
|
26
|
+
- [Get a task done end to end](#get-a-task-done-end-to-end): lines 72–94. Start a task, attach results and hand it off for review.
|
|
27
|
+
- [Track your progress in Takt](#track-your-progress-in-takt): lines 95–106. Record milestones, session pointers, outputs and follow-up work.
|
|
28
|
+
- [Find what the wiki knows](#find-what-the-wiki-knows): lines 107–118. Choose literal, keyword or semantic search and read relevant sections.
|
|
29
|
+
- [Write back what you learned](#write-back-what-you-learned): lines 119–129. Update, create, verify or flag wiki knowledge before review.
|
|
30
|
+
- [Recover from an error](#recover-from-an-error): lines 130–143. Diagnose status, dependency, validation, CLI and transport failures.
|
|
31
|
+
- [Don't use](#dont-use): lines 144–149. Replace deprecated task and wiki commands with supported verbs.
|
|
32
|
+
- [Maintain this skill](#maintain-this-skill): lines 150–156. Recalculate indexes, validate the contract and locate the canonical source.
|
|
33
|
+
|
|
34
|
+
## Quick start
|
|
35
|
+
|
|
36
|
+
1. Check `taktcli --version` and `taktcli whoami`; resolve credentials, project and wiki with [Setup](#setup).
|
|
37
|
+
2. Choose a [mode](#modes) and read its reference. Use each reference's index to fetch only the chapter you need.
|
|
38
|
+
3. Read current state: `taktcli get <id>` for a task, `taktcli board --project <p>` for a board, or `taktcli wiki search "topic" --wiki <w>` for knowledge.
|
|
39
|
+
4. Before changing a task, read the [rules](#rules). The worker loop is `run` → implement and verify → attach results → REVIEW; never take a DRAFT task.
|
|
40
|
+
|
|
18
41
|
## Modes
|
|
19
42
|
|
|
20
43
|
| Invoke | Alias | Use it to | Read |
|
|
@@ -123,3 +146,11 @@ More: [cli.md](references/cli.md#errors).
|
|
|
123
146
|
- `claim`, `release`, `--lease`: deprecated aliases. Use `run`, `stop`, nothing.
|
|
124
147
|
- `workspace list|create|get`: use `wikis`.
|
|
125
148
|
- `status <id> CANCELLED`: use `task transition <id> cancel`.
|
|
149
|
+
|
|
150
|
+
## Maintain this skill
|
|
151
|
+
|
|
152
|
+
Every agent-facing Markdown file over 100 physical lines needs a complete `## Index` within its first 100 lines. Index every body `##` chapter with an anchor, inclusive line range and reason to read it. Keep this entrypoint's complete quick start within the same budget. Short references can stay unindexed.
|
|
153
|
+
|
|
154
|
+
After editing, run this skill's `scripts/markdown_index.py` with the existing Python interpreter, then repeat with `--check`. It updates ranges in existing entries; authors maintain chapter links and descriptions. Run `--check --quick-start SKILL.md` from the skill directory to check the entrypoint's quick-start budget. Explicit paths resolve from the caller's working directory; the helper's default scope is its own skill's `SKILL.md` and `references/`.
|
|
155
|
+
|
|
156
|
+
For repo edits, the canonical source, repository links, tests and publication are covered in [plumbing.md](references/plumbing.md#maintaining-the-skill).
|
|
@@ -2,14 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
Look up a verb, a flag, an output shape or an error. The tables cover real usage; `taktcli <noun> <verb> --help` is the ground truth for the installed version.
|
|
4
4
|
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
-
|
|
8
|
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
5
|
+
## Index
|
|
6
|
+
|
|
7
|
+
Read a chapter with `sed -n 'A,Bp'` on this file.
|
|
8
|
+
|
|
9
|
+
- [Contract](#contract): lines 17–30. Check installation, authentication, output, shell and identifier conventions.
|
|
10
|
+
- [Verbs](#verbs): lines 31–44. Find the supported command groups and their verbs.
|
|
11
|
+
- [Quick reference](#quick-reference): lines 45–73. Look up common task, wiki, file and administration commands.
|
|
12
|
+
- [Ordering and paging](#ordering-and-paging): lines 74–87. Choose sorting, limits and offsets and interpret result envelopes.
|
|
13
|
+
- [Errors](#errors): lines 88–103. Recover from GraphQL, transport and usage failures.
|
|
14
|
+
- [Verbs that don't exist](#verbs-that-dont-exist): lines 104–118. Replace common invented commands and flags with supported forms.
|
|
15
|
+
- [FAQ](#faq): lines 119–129. Resolve project and wiki selection, large text and command discovery questions.
|
|
13
16
|
|
|
14
17
|
## Contract
|
|
15
18
|
|
|
@@ -122,5 +125,5 @@ Don't use: `claim` / `release` / `--lease` (deprecated aliases of `run` / `stop`
|
|
|
122
125
|
| 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
126
|
| does `run` lock the task? | no. Whoever dispatches decides who works it; check before starting a RUNNING task. |
|
|
124
127
|
| 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). |
|
|
128
|
+
| 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
129
|
| find mentions of a task | `wiki grep "web-42" --wiki <w>` (literal; never `--regex` on `[[…]]`) |
|
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
# Plumbing — auth, output, gql, files, admin
|
|
2
2
|
|
|
3
|
-
##
|
|
4
|
-
|
|
5
|
-
-
|
|
6
|
-
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
3
|
+
## Index
|
|
4
|
+
|
|
5
|
+
Read a chapter with `sed -n 'A,Bp'` on this file.
|
|
6
|
+
|
|
7
|
+
- [Auth](#auth): lines 17–43. Resolve credentials and endpoints, log in and handle refresh failures.
|
|
8
|
+
- [Output and exit codes](#output-and-exit-codes): lines 44–50. Parse stdout and stderr and branch on the command's exit code.
|
|
9
|
+
- [Output shapes](#output-shapes): lines 51–62. Find result fields for common queries, search and grep.
|
|
10
|
+
- [gql and schema](#gql-and-schema): lines 63–78. Discover the schema and pass typed variables or large text to GraphQL.
|
|
11
|
+
- [Files and media](#files-and-media): lines 79–91. Upload, download, attach and reference media files.
|
|
12
|
+
- [Projects, labels, wikis](#projects-labels-wikis): lines 92–118. Manage project metadata, labels and owner-gated wiki rename or deletion.
|
|
13
|
+
- [Defaults and versions](#defaults-and-versions): lines 119–123. Check default limits and which installed CLI version is running.
|
|
14
|
+
- [The bundled skill](#the-bundled-skill): lines 124–139. Show or install packaged skills and understand overwrite protection.
|
|
15
|
+
- [Maintaining the skill](#maintaining-the-skill): lines 140–156. Update source indexes, run checks and keep distributed copies accurate.
|
|
12
16
|
|
|
13
17
|
## Auth
|
|
14
18
|
|
|
@@ -102,12 +106,15 @@ taktcli wikis get <project-slug>
|
|
|
102
106
|
taktcli wikis update <slug> --name N | --description TEXT | --description-file d.md
|
|
103
107
|
taktcli wikis rename-preview <slug> --new-slug S
|
|
104
108
|
taktcli wikis update <slug> --new-slug S --yes-rewrite-links [--name N …]
|
|
109
|
+
taktcli wikis delete <slug> --dry-run
|
|
110
|
+
taktcli wikis delete <slug> --yes
|
|
105
111
|
```
|
|
106
112
|
|
|
107
113
|
- `wikis create` makes a wiki; `wiki create` makes a page.
|
|
108
114
|
- `wikis update` needs an explicit slug and at least one field; omitted fields stay.
|
|
109
115
|
- 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
116
|
- `wikis rename-preview` returns the same report without writing anything. Run it first, and update the external config it lists after the rename.
|
|
117
|
+
- `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
118
|
|
|
112
119
|
## Defaults and versions
|
|
113
120
|
|
|
@@ -129,3 +136,21 @@ taktcli skill install --aliases # also takt-cli, explore-wiki, takt-w
|
|
|
129
136
|
```
|
|
130
137
|
|
|
131
138
|
Install copies the files and writes `VERSION` (the version, then a `sha256 path` line per file). It refuses to replace a copy that was edited, has no `VERSION`, is a symlink, or comes from a newer taktcli; `--force` overrides. An identical copy is left alone (`"action": "unchanged"`).
|
|
139
|
+
|
|
140
|
+
## Maintaining the skill
|
|
141
|
+
|
|
142
|
+
The canonical source is `cli/taktcli/skills/`, shipped directly in the wheel. `.claude/skills/takt` links to the package's `takt` directory, and each repo alias's `SKILL.md` links to its package entrypoint. Edit the package sources on a task branch; there is no copy or sync step. The explore-wiki hook stays repo-only under `.claude/skills/explore-wiki/hooks/`. Use the project's existing Python interpreter; the bundled `scripts/markdown_index.py` needs only the standard library.
|
|
143
|
+
|
|
144
|
+
From the repo root:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
npm run skills:index
|
|
148
|
+
npm run skills:check
|
|
149
|
+
npm run test:cli
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
`skills:index` recalculates chapter ranges in the source entrypoint and references. Authors add or remove entries and write their descriptions; the helper only replaces numbers. A chapter starts at its `##` heading and ends before the next `##`, or at EOF. Fenced examples and frontmatter do not create chapters, but count toward physical line numbers.
|
|
153
|
+
|
|
154
|
+
`skills:check` validates the canonical indexes without writing. The CLI tests enforce complete coverage, exact ranges, the early-index budget and the entrypoint's complete quick start by line 100. They also check repository links, parser regressions and the text returned by `skill show` and copied by `skill install`. These checks run in forge PR CI and the CLI release workflow.
|
|
155
|
+
|
|
156
|
+
Recalculate after the final content edit, then check and test before publishing the task branch and PR. The wheel ships the updated skill on the next CLI release; a repo edit alone does not update an installed package or a separately registered platform copy. If publishing to another surface, validate the exact distributed Markdown: inserted headers or reflowed text invalidate its ranges.
|
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
# Tasks — states, start/stop, pointers, results, deps, watch
|
|
2
2
|
|
|
3
|
-
##
|
|
4
|
-
|
|
5
|
-
-
|
|
6
|
-
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
3
|
+
## Index
|
|
4
|
+
|
|
5
|
+
Read a chapter with `sed -n 'A,Bp'` on this file.
|
|
6
|
+
|
|
7
|
+
- [Statuses and the two lanes](#statuses-and-the-two-lanes): lines 17–47. Choose legal status edges or lifecycle actions and check dependency gates.
|
|
8
|
+
- [Pick from the board](#pick-from-the-board): lines 48–59. Find ready work and re-fetch a task before acting.
|
|
9
|
+
- [Start, stop, hand back](#start-stop-hand-back): lines 60–70. Start or release work without locks, leases or ownership assumptions.
|
|
10
|
+
- [Session pointers in meta](#session-pointers-in-meta): lines 71–96. Record a session under its tool and role without overwriting another worker.
|
|
11
|
+
- [Finish in order](#finish-in-order): lines 97–111. Attach outputs before review and use DONE only within its authorized scope.
|
|
12
|
+
- [Results and comments](#results-and-comments): lines 112–126. Read dependency outputs, attach results and post feedback.
|
|
13
|
+
- [Dependencies and subtasks](#dependencies-and-subtasks): lines 127–141. Inspect or update dependencies and create or reparent subtasks.
|
|
14
|
+
- [Watch for events](#watch-for-events): lines 142–154. Stream changes and handle reconnects and tasks already ready at startup.
|
|
15
|
+
- [Notifications](#notifications): lines 155–162. Inspect unread notifications and mark them read.
|
|
13
16
|
|
|
14
17
|
## Statuses and the two lanes
|
|
15
18
|
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
# Wiki — find, read, write, flag
|
|
2
2
|
|
|
3
|
-
##
|
|
4
|
-
|
|
5
|
-
-
|
|
6
|
-
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
3
|
+
## Index
|
|
4
|
+
|
|
5
|
+
Read a chapter with `sed -n 'A,Bp'` on this file.
|
|
6
|
+
|
|
7
|
+
- [Find the wiki](#find-the-wiki): lines 15–27. Resolve the wiki slug from instructions, a project or the wiki list.
|
|
8
|
+
- [List pages](#list-pages): lines 28–40. Filter pages by folder, labels, status or content and interpret the envelope.
|
|
9
|
+
- [Search](#search): lines 41–56. Choose grep, keyword, semantic or related-task search and page through hits.
|
|
10
|
+
- [Read](#read): lines 57–71. Fetch a page, heading or line slice and follow relevant mentions.
|
|
11
|
+
- [Write back](#write-back): lines 72–87. Edit, create, move or retire knowledge using the appropriate verb.
|
|
12
|
+
- [Flag what you can't fix](#flag-what-you-cant-fix): lines 88–107. Label knowledge problems and inspect search feedback.
|
|
13
|
+
- [Conventions](#conventions): lines 108–116. Check page paths, index links, mentions, media and publication status.
|
|
11
14
|
|
|
12
15
|
## Find the wiki
|
|
13
16
|
|
|
@@ -7,18 +7,21 @@ Pick or receive a task, groom it if needed, start it, do the work, attach result
|
|
|
7
7
|
/takt worker <project> work the project's READY tasks in priority order
|
|
8
8
|
```
|
|
9
9
|
|
|
10
|
-
##
|
|
11
|
-
|
|
12
|
-
-
|
|
13
|
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
-
|
|
10
|
+
## Index
|
|
11
|
+
|
|
12
|
+
Read a chapter with `sed -n 'A,Bp'` on this file.
|
|
13
|
+
|
|
14
|
+
- [1. Resolve](#1-resolve): lines 26–34. Resolve the task or project argument and its wiki.
|
|
15
|
+
- [2. Pick a task](#2-pick-a-task): lines 35–54. Select eligible ready work without taking another worker's task.
|
|
16
|
+
- [3. Groom if vague](#3-groom-if-vague): lines 55–66. Enrich unclear work or split it before starting implementation.
|
|
17
|
+
- [4. Start and record a pointer](#4-start-and-record-a-pointer): lines 67–86. Re-fetch, start and record an orchestrator-provided session pointer.
|
|
18
|
+
- [5. Load context](#5-load-context): lines 87–98. Read task feedback, dependencies and relevant wiki knowledge.
|
|
19
|
+
- [6. Implement and verify](#6-implement-and-verify): lines 99–112. Isolate the branch, verify scope and tests and publish its PR.
|
|
20
|
+
- [7. Finish: results, wiki, REVIEW](#7-finish-results-wiki-review): lines 113–128. Attach outputs and write back knowledge before the review hand-off.
|
|
21
|
+
- [8. Handle review feedback](#8-handle-review-feedback): lines 129–140. Verify findings, publish fixes and respect merge authority.
|
|
22
|
+
- [9. Hand back or fail](#9-hand-back-or-fail): lines 141–150. Explain blockers, release work or mark it failed.
|
|
23
|
+
- [10. Next task](#10-next-task): lines 151–158. Refresh the board and sequence dependent tasks within the authorized scope.
|
|
24
|
+
- [Recover from an error](#recover-from-an-error): lines 159–169. Handle lifecycle, dependency, test and context failures.
|
|
22
25
|
|
|
23
26
|
## 1. Resolve
|
|
24
27
|
|
|
@@ -105,7 +108,7 @@ Then read the tasks and `[[wiki/path]]` pages the description mentions, and run
|
|
|
105
108
|
| frontend | if the project ships design guidance and a design skill, use them for the visual work, then finish wiring and tests yourself |
|
|
106
109
|
| progress | `taktcli task comment <id> --body-file note.md` at milestones and decisions |
|
|
107
110
|
|
|
108
|
-
Commit in small steps
|
|
111
|
+
Verify the repo, assigned task branch, remote, PR base and staged diff. Commit in small steps and push the task branch. Update the existing PR for that head/base, or open one (body via a file). Follow an explicit draft, stacked-base, branch-only or no-PR override.
|
|
109
112
|
|
|
110
113
|
## 7. Finish: results, wiki, REVIEW
|
|
111
114
|
|
|
@@ -151,7 +154,7 @@ Feedback arrives as PR comments, task comments, or the task returning to READY.
|
|
|
151
154
|
2. Low on context → finish the current task cleanly (commit, REVIEW), report, suggest a fresh session.
|
|
152
155
|
3. Otherwise back to step 2, preferring related tasks.
|
|
153
156
|
|
|
154
|
-
Several related tasks with one PR each (an epic's subtasks): finish one through REVIEW
|
|
157
|
+
Several related tasks with one PR each (an epic's subtasks): finish one through REVIEW, then wait for an authorized merge before starting a dependent task, or stack the next PR on the specified branch if instructed. One PR per task keeps reviews small; bundle only mechanically identical changes. Load shared context once for the whole run.
|
|
155
158
|
|
|
156
159
|
## Recover from an error
|
|
157
160
|
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import re
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
HEAD_LINES = 100
|
|
8
|
+
ROOT = Path(__file__).resolve().parents[1]
|
|
9
|
+
HEADING = re.compile(r'^ {0,3}(#{1,6})\s+(.+?)\s*#*\s*$')
|
|
10
|
+
FENCE = re.compile(r'^ {0,3}(`{3,}|~{3,})(.*)$')
|
|
11
|
+
ENTRY = re.compile(r'^- \[(?P<label>[^\]]+)\]\(#(?P<anchor>[^)]+)\): lines (?P<start>\d+)[–-](?P<end>\d+)\. (?P<summary>\S.*)$')
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def headings(lines):
|
|
15
|
+
result = []
|
|
16
|
+
used = set()
|
|
17
|
+
fence = None
|
|
18
|
+
frontmatter = bool(lines and lines[0].strip() == '---')
|
|
19
|
+
for number, raw in enumerate(lines, 1):
|
|
20
|
+
line = raw.rstrip('\r\n')
|
|
21
|
+
if frontmatter:
|
|
22
|
+
if number > 1 and line.strip() in ('---', '...'):
|
|
23
|
+
frontmatter = False
|
|
24
|
+
continue
|
|
25
|
+
marker = FENCE.match(line)
|
|
26
|
+
if marker:
|
|
27
|
+
token, suffix = marker.groups()
|
|
28
|
+
if fence is None:
|
|
29
|
+
fence = token
|
|
30
|
+
elif token[0] == fence[0] and len(token) >= len(fence) and not suffix.strip():
|
|
31
|
+
fence = None
|
|
32
|
+
continue
|
|
33
|
+
match = HEADING.match(line)
|
|
34
|
+
if fence or not match:
|
|
35
|
+
continue
|
|
36
|
+
level, title = len(match[1]), match[2]
|
|
37
|
+
visible = re.sub(r'!?\[([^\]]+)\]\([^)]*\)', r'\1', title)
|
|
38
|
+
base = re.sub(r'\s', '-', re.sub(r'[^\w\s-]', '', visible.lower()))
|
|
39
|
+
slug = base
|
|
40
|
+
suffix = 0
|
|
41
|
+
while slug in used:
|
|
42
|
+
suffix += 1
|
|
43
|
+
slug = f'{base}-{suffix}'
|
|
44
|
+
used.add(slug)
|
|
45
|
+
result.append((number, level, title, slug))
|
|
46
|
+
return result
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def structure(text):
|
|
50
|
+
lines = text.splitlines(keepends=True)
|
|
51
|
+
chapters = [heading for heading in headings(lines) if heading[1] == 2]
|
|
52
|
+
indexes = [heading for heading in chapters if heading[2] == 'Index']
|
|
53
|
+
sections = {}
|
|
54
|
+
for position, (start, _, title, slug) in enumerate(chapters):
|
|
55
|
+
if title == 'Index':
|
|
56
|
+
continue
|
|
57
|
+
end = chapters[position + 1][0] - 1 if position + 1 < len(chapters) else len(lines)
|
|
58
|
+
sections[slug] = (title, start, end)
|
|
59
|
+
entries = []
|
|
60
|
+
if len(indexes) == 1:
|
|
61
|
+
start = indexes[0][0]
|
|
62
|
+
end = next((heading[0] - 1 for heading in chapters if heading[0] > start), len(lines))
|
|
63
|
+
entries = [(number, ENTRY.fullmatch(lines[number - 1].rstrip('\r\n')))
|
|
64
|
+
for number in range(start + 1, end + 1)
|
|
65
|
+
if lines[number - 1].startswith('- ')]
|
|
66
|
+
return lines, chapters, indexes, sections, entries
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def document_errors(text, *, check_ranges=True, quick_start=False):
|
|
70
|
+
lines, chapters, indexes, sections, entries = structure(text)
|
|
71
|
+
if len(lines) <= HEAD_LINES and not indexes:
|
|
72
|
+
return ['An indexed ## Quick start is required.'] if quick_start else []
|
|
73
|
+
if len(indexes) != 1:
|
|
74
|
+
return ['Expected exactly one ## Index for a document longer than 100 lines.']
|
|
75
|
+
errors = []
|
|
76
|
+
if not sections:
|
|
77
|
+
errors.append('Add meaningful ## chapters for the index to address.')
|
|
78
|
+
if chapters[0] != indexes[0]:
|
|
79
|
+
errors.append('Place ## Index before the first body chapter.')
|
|
80
|
+
if not entries or any(match is None for _, match in entries):
|
|
81
|
+
return errors + ['Use - [Chapter](#anchor): lines A–B. When to read it. for every entry.']
|
|
82
|
+
index_end = next((heading[0] - 1 for heading in chapters if heading[0] > indexes[0][0]), len(lines))
|
|
83
|
+
if index_end > HEAD_LINES:
|
|
84
|
+
errors.append('The complete index must fit within the first 100 lines.')
|
|
85
|
+
anchors = [match['anchor'] for _, match in entries]
|
|
86
|
+
if len(anchors) != len(set(anchors)):
|
|
87
|
+
errors.append('A chapter appears more than once in the index.')
|
|
88
|
+
missing = sections.keys() - set(anchors)
|
|
89
|
+
unknown = set(anchors) - sections.keys()
|
|
90
|
+
if missing:
|
|
91
|
+
errors.append(f'Unindexed chapters: {", ".join(sorted(missing))}.')
|
|
92
|
+
if unknown:
|
|
93
|
+
errors.append(f'Unknown chapter anchors: {", ".join(sorted(unknown))}.')
|
|
94
|
+
if check_ranges:
|
|
95
|
+
for _, match in entries:
|
|
96
|
+
if match['anchor'] in sections:
|
|
97
|
+
title, start, end = sections[match['anchor']]
|
|
98
|
+
if (int(match['start']), int(match['end'])) != (start, end):
|
|
99
|
+
errors.append(f'{title}: expected lines {start}–{end}; run markdown_index.py.')
|
|
100
|
+
if quick_start and ('quick-start' not in sections or sections['quick-start'][2] > HEAD_LINES):
|
|
101
|
+
errors.append('The complete ## Quick start must fit within the first 100 lines.')
|
|
102
|
+
return errors
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def reindex(text):
|
|
106
|
+
errors = document_errors(text, check_ranges=False)
|
|
107
|
+
if errors:
|
|
108
|
+
raise ValueError('\n'.join(errors))
|
|
109
|
+
lines, _, _, sections, entries = structure(text)
|
|
110
|
+
for number, match in entries:
|
|
111
|
+
_, start, end = sections[match['anchor']]
|
|
112
|
+
line = lines[number - 1]
|
|
113
|
+
lines[number - 1] = line[:match.start('start')] + f'{start}–{end}' + line[match.end('end'):]
|
|
114
|
+
return ''.join(lines)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def documents(paths):
|
|
118
|
+
return sorted({file for path in paths for file in (path.rglob('*.md') if path.is_dir() else [path])})
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def main():
|
|
122
|
+
parser = argparse.ArgumentParser(description='Recalculate or check early chapter indexes in Markdown files over 100 lines.')
|
|
123
|
+
parser.add_argument('paths', nargs='*', type=Path, help='Files or directories; default: this skill and its references.')
|
|
124
|
+
parser.add_argument('--check', action='store_true', help='Report missing indexes and stale ranges without writing files.')
|
|
125
|
+
parser.add_argument('--quick-start', action='store_true', help='Also require an indexed Quick start ending by line 100.')
|
|
126
|
+
args = parser.parse_args()
|
|
127
|
+
failures = []
|
|
128
|
+
files = documents(args.paths or [ROOT / 'SKILL.md', ROOT / 'references'])
|
|
129
|
+
for path in files:
|
|
130
|
+
with path.open(encoding='utf-8', newline='') as source:
|
|
131
|
+
text = source.read()
|
|
132
|
+
errors = document_errors(text, check_ranges=args.check, quick_start=args.quick_start)
|
|
133
|
+
failures.extend(f'{path}: {error}' for error in errors)
|
|
134
|
+
if not args.check and not errors:
|
|
135
|
+
updated = reindex(text)
|
|
136
|
+
if updated != text:
|
|
137
|
+
with path.open('w', encoding='utf-8', newline='') as target:
|
|
138
|
+
target.write(updated)
|
|
139
|
+
print(f'Updated {path}')
|
|
140
|
+
if failures:
|
|
141
|
+
print('\n'.join(failures), file=sys.stderr)
|
|
142
|
+
return 1
|
|
143
|
+
print(f'{"Checked" if args.check else "Indexed"} {len(files)} Markdown files.')
|
|
144
|
+
return 0
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
if __name__ == '__main__':
|
|
148
|
+
raise SystemExit(main())
|