@erclx/aitk 0.20.0 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/claude-address-review/REQUIREMENT.md +1 -0
- package/claude/skills/claude-address-review/SKILL.md +4 -3
- package/claude/skills/claude-diagram/SKILL.md +1 -1
- package/claude/skills/claude-pr-review/REQUIREMENT.md +1 -0
- package/claude/skills/claude-pr-review/SKILL.md +1 -1
- package/claude/skills/git-followup/REQUIREMENT.md +1 -1
- package/claude/skills/git-followup/SKILL.md +1 -1
- package/claude/skills/git-issue/REQUIREMENT.md +1 -1
- package/claude/skills/git-issue/SKILL.md +1 -1
- package/claude/skills/git-pr/REQUIREMENT.md +1 -1
- package/claude/skills/git-pr/SKILL.md +1 -1
- package/claude/skills/git-split/REQUIREMENT.md +1 -1
- package/claude/skills/git-split/SKILL.md +1 -1
- package/docs/agents.md +39 -0
- package/docs/target-projects.md +1 -1
- package/package.json +1 -1
- package/scripts/core/verify.sh +9 -0
- package/scripts/lib/sandbox-path.sh +75 -6
- package/src/cli.ts +4 -0
- package/src/commands/context.ts +321 -0
- package/src/context/audit.ts +227 -0
- package/src/context/citations.ts +167 -0
- package/src/context/folders.ts +91 -0
- package/src/context/index-drift.ts +64 -0
- package/standards/index.md +1 -1
- package/standards/prose.md +16 -2
- package/standards/versioning.md +11 -1
|
@@ -17,6 +17,7 @@ Without this skill, review findings are worked in whatever order the author read
|
|
|
17
17
|
- Push before replying, so the comment never runs ahead of the code it describes
|
|
18
18
|
- Map every finding to what changed, or to a one-line reason when it is a question or a conscious accept
|
|
19
19
|
- Post the terminal comment only when the findings are addressed and every check passes
|
|
20
|
+
- Scan the reply for banned characters and internal phase labels before posting, since the comment leaves for the remote unchecked
|
|
20
21
|
|
|
21
22
|
## Must not
|
|
22
23
|
|
|
@@ -73,9 +73,10 @@ bullet per finding, each opening with the bolded finding identifier.
|
|
|
73
73
|
Close the body with `🤖 Addressed by Claude Code` on its own line so the reply
|
|
74
74
|
reads as an independent machine pass, not a human sign-off.
|
|
75
75
|
|
|
76
|
-
Before posting, run the
|
|
77
|
-
against the reply
|
|
78
|
-
|
|
76
|
+
Before posting, run the pre-publish scan in `.claude/standards/prose.md`
|
|
77
|
+
against the reply, or `${CLAUDE_SKILL_DIR}/../../standards/prose.md` when the
|
|
78
|
+
project does not have it. The hook skips `.claude/.tmp/`, so this scan is the
|
|
79
|
+
only gate on the published reply. Post it to the PR:
|
|
79
80
|
|
|
80
81
|
```bash
|
|
81
82
|
gh pr comment <number> --body-file .claude/.tmp/address-review/reply-<number>.md
|
|
@@ -80,7 +80,7 @@ The code paths the explanation cites are what the `claude-docs` sweep watches fo
|
|
|
80
80
|
|
|
81
81
|
Quote node labels containing spaces or special characters with double quotes (`A["Web shell"]`). Avoid parentheses inside labels, they break some renderers. Use `<br/>` for line breaks inside labels.
|
|
82
82
|
|
|
83
|
-
Run the
|
|
83
|
+
Run the pre-publish scan in `.claude/standards/prose.md` against the whole file, including inside mermaid `subgraph` labels and node text, or `${CLAUDE_SKILL_DIR}/../../standards/prose.md` when the project does not have it. The file lands in the repository rather than on a remote, so the scan's destination scoping leaves the character checks and not the phase-label one. A colon also works as a replacement inside a label. The pedagogical voice the diagram standard asks for is a yield on voice alone and buys no exemption from these bans.
|
|
84
84
|
|
|
85
85
|
Nothing enforces the ban inside the diagram. The standards-audit hook toggles on a fence and skips every line within it, so a label carrying an em dash passes silently while the same character in the explanation paragraph below is caught. Check the labels by reading them. This is the one place in the file where the author is the only gate.
|
|
86
86
|
|
|
@@ -17,6 +17,7 @@ Without this skill, a pull request is reviewed only by the session that wrote it
|
|
|
17
17
|
- Apply the integration, contract, and consumer lenses a self-review structurally cannot
|
|
18
18
|
- Post a close-out even with nothing to report, since a first pass left unanswered reads as a review nobody closed
|
|
19
19
|
- Key the body file on both the pull request number and the head commit, so no two passes overwrite each other
|
|
20
|
+
- Scan the comment for banned characters and internal phase labels before posting, since a finding phrased against a phase label reaches a reader with no task board
|
|
20
21
|
|
|
21
22
|
## Must not
|
|
22
23
|
|
|
@@ -131,7 +131,7 @@ The `What is right` section is optional, capped at three bullets, and included o
|
|
|
131
131
|
|
|
132
132
|
Close the body with `🤖 Reviewed by Claude Code` on its own line so the review reads as an independent machine pass, not a human sign-off.
|
|
133
133
|
|
|
134
|
-
Before posting, run the
|
|
134
|
+
Before posting, run the pre-publish scan in `.claude/standards/prose.md` against the body, or `${CLAUDE_SKILL_DIR}/../../standards/prose.md` when the project does not have it. The hook skips `.claude/.tmp/`, so this scan is the only gate on the published comment. A finding phrased against an internal phase label is what the label half of the scan catches here.
|
|
135
135
|
|
|
136
136
|
```bash
|
|
137
137
|
gh pr review <number> --comment --body-file .claude/.tmp/pr-review/body-<number>-<short-sha>.md
|
|
@@ -14,7 +14,7 @@ Without this skill, an edit made after a pull request is already open ships as a
|
|
|
14
14
|
- Refuse unless the branch has an upstream and an open pull request, since every later step addresses one of the two
|
|
15
15
|
- Delegate the message to `git-commit` rather than composing one, so a follow-up commit reads like every other commit
|
|
16
16
|
- Sync the body when the new commit moves the scope, and the title when the shift makes it inaccurate
|
|
17
|
-
- Reply on the pull request when it carries review comments, and scan that reply for the banned characters before posting. The hook watches files and never sees a comment body on its way to the remote.
|
|
17
|
+
- Reply on the pull request when it carries review comments, and scan that reply for the banned characters and internal phase labels before posting. The hook watches files and never sees a comment body on its way to the remote.
|
|
18
18
|
- Suppress the reply when the caller owns it, and still run the push and the body sync
|
|
19
19
|
|
|
20
20
|
## Must not
|
|
@@ -24,7 +24,7 @@ its own reply, so skip the comment in step 6. The push and body sync still run.
|
|
|
24
24
|
3. Invoke `aitk:git-commit` to generate one conventional commit from the staged diff
|
|
25
25
|
4. Run `git push` to the tracking branch
|
|
26
26
|
5. Check for existing review comments: `gh api 'repos/{owner}/{repo}/pulls/<number>/comments' --jq 'length'`, resolving `<number>` from `gh pr view --json number`.
|
|
27
|
-
6. When invoked with `reply-owned`, skip this step's comment: the caller posts the reply. Otherwise, if the count is above zero, the followup addresses review feedback: post a one-line summary of the fix with `gh pr comment --body`, first running the
|
|
27
|
+
6. When invoked with `reply-owned`, skip this step's comment: the caller posts the reply. Otherwise, if the count is above zero, the followup addresses review feedback: post a one-line summary of the fix with `gh pr comment --body`, first running the pre-publish scan in `.claude/standards/prose.md` against it, or `${CLAUDE_SKILL_DIR}/../../standards/prose.md` when the project does not have it, since the hook does not see an inline comment body. If it is zero, run `gh pr view --json url,title,body` and update the body with `gh pr edit --body` when the new commit changes scope, and the title with `gh pr edit --title` when the scope shifted enough to make it inaccurate.
|
|
28
28
|
|
|
29
29
|
## After completion
|
|
30
30
|
|
|
@@ -13,7 +13,7 @@ Without this skill, an issue takes the shape of whatever the session was saying.
|
|
|
13
13
|
|
|
14
14
|
- Read the issue reference and the prose standard before formatting
|
|
15
15
|
- Confirm a remote and an authenticated `gh` before composing, so the failure lands before a body is written rather than after
|
|
16
|
-
- Scan the title and body for banned characters as an explicit step. Reading the standard does not catch them, because the text is generated after the read.
|
|
16
|
+
- Scan the title and body for banned characters and internal phase labels as an explicit step. Reading the standard does not catch them, because the text is generated after the read.
|
|
17
17
|
- Map the type to exactly one label, so filing and filtering agree
|
|
18
18
|
- Ask once when the type is ambiguous between a bug and a task, since the label follows from it and a wrong label buries the issue
|
|
19
19
|
- Pass the body through a file and remove it afterward, so shell quoting cannot mangle it
|
|
@@ -40,7 +40,7 @@ After outputting the preview, execute the final command immediately. Claude Code
|
|
|
40
40
|
|
|
41
41
|
### Pre-publish scan
|
|
42
42
|
|
|
43
|
-
Before running the final command, run the
|
|
43
|
+
Before running the final command, run the pre-publish scan in `.claude/standards/prose.md` against the title and body, or `${CLAUDE_SKILL_DIR}/../../standards/prose.md` when the project does not have it. The title and body go straight to the remote with nothing checking them on the way, so this scan is the only gate. It covers the phase-label check as well as the characters, since both go to a reader who has no task board. It applies on top of the banned phrases in `${CLAUDE_SKILL_DIR}/references/issue.md`.
|
|
44
44
|
|
|
45
45
|
### Final command
|
|
46
46
|
|
|
@@ -15,7 +15,7 @@ Without this skill, a pull request body is written from memory of the branch rat
|
|
|
15
15
|
- Derive the title and body from the commits and the diff against main, with lockfiles excluded
|
|
16
16
|
- Run each check before writing its line, then state the result the run reported
|
|
17
17
|
- Leave a box unchecked only for a human-only case, naming which human and why on the same line
|
|
18
|
-
- Scan the title and body for banned characters as an explicit step, on top of reading the prose standard
|
|
18
|
+
- Scan the title and body for banned characters and internal phase labels as an explicit step, on top of reading the prose standard
|
|
19
19
|
- Detect an open pull request and edit it in place, so a follow-up push keeps the body in sync instead of failing
|
|
20
20
|
|
|
21
21
|
## Must not
|
|
@@ -69,7 +69,7 @@ Leave a box unchecked only for the human-only cases the reference defines, and n
|
|
|
69
69
|
|
|
70
70
|
### Pre-publish scan
|
|
71
71
|
|
|
72
|
-
Before running the final command, run the
|
|
72
|
+
Before running the final command, run the pre-publish scan in `.claude/standards/prose.md` against the PR title and body, or `${CLAUDE_SKILL_DIR}/../../standards/prose.md` when the project does not have it. The title and body go straight to the remote with nothing checking them on the way, so this scan is the only gate. It covers the phase-label check as well as the characters, since both go to a reader who has no task board. It applies on top of the banned phrases in `${CLAUDE_SKILL_DIR}/references/pr.md`.
|
|
73
73
|
|
|
74
74
|
### Final command
|
|
75
75
|
|
|
@@ -17,7 +17,7 @@ Without this skill, a branch carrying unrelated commits ships as one pull reques
|
|
|
17
17
|
- Account for every commit ahead of main in the preview
|
|
18
18
|
- State the merge order and the reason behind it, since the order is not recoverable from reading the pull requests afterward
|
|
19
19
|
- Carry the restack loop for stacked mode, where each branch is rebased onto main once the one below it merges
|
|
20
|
-
- Scan every generated title and body for banned characters as an explicit step before opening a pull request
|
|
20
|
+
- Scan every generated title and body for banned characters and internal phase labels as an explicit step before opening a pull request
|
|
21
21
|
|
|
22
22
|
## Must not
|
|
23
23
|
|
|
@@ -69,7 +69,7 @@ If Mode is Independent, append this line to the preview:
|
|
|
69
69
|
|
|
70
70
|
After outputting the preview, execute the final commands immediately. Claude Code's tool permission dialog is the confirmation gate. Do not wait for user input.
|
|
71
71
|
|
|
72
|
-
Before running them, run the
|
|
72
|
+
Before running them, run the pre-publish scan in `.claude/standards/prose.md` against every title and body, or `${CLAUDE_SKILL_DIR}/../../standards/prose.md` when the project does not have it. Each body stages under `.claude/.tmp/` and leaves through `gh`, so the hook sees neither and this scan is the only gate.
|
|
73
73
|
|
|
74
74
|
### Final commands
|
|
75
75
|
|
package/docs/agents.md
CHANGED
|
@@ -80,6 +80,7 @@ Full help: `aitk <command> --help`.
|
|
|
80
80
|
| `aitk transcripts <url>` | Fetch a YouTube transcript with metadata frontmatter (needs `yt-dlp`) |
|
|
81
81
|
| `aitk tasks archive` | Move a shipped task off the board, clear its ordering row, and regenerate the index |
|
|
82
82
|
| `aitk comments scan` | Measure comment density by language and comment kind, with a trend recomputed from git |
|
|
83
|
+
| `aitk context audit` | Report entry length, depth, cited-path resolution, and index drift in the context folders |
|
|
83
84
|
| `aitk capture [source]` | Render HTML capture sources to PNG, toolkit-only and absent from an installed package |
|
|
84
85
|
|
|
85
86
|
### Domain commands
|
|
@@ -98,6 +99,7 @@ Each domain exposes a consistent shape where applicable: `list`, `install`, `syn
|
|
|
98
99
|
| `slides` | `render`, `list` |
|
|
99
100
|
| `tasks` | `archive` |
|
|
100
101
|
| `comments` | `scan` |
|
|
102
|
+
| `context` | `audit` |
|
|
101
103
|
|
|
102
104
|
Common patterns:
|
|
103
105
|
|
|
@@ -351,6 +353,43 @@ The degradation sweep reads its vocabulary from whichever rule publishes a `## D
|
|
|
351
353
|
|
|
352
354
|
`090-code-comments` is the rule that publishes the list, and it ships on the `base` stack. A project that installs or syncs governance for the first time after that rule landed gets a sweep that previously reported skipped, so hits appear where the command used to stay quiet. Edit the backticked terms in the installed copy to change what that project sweeps for. The sweep matches comment text, so a comment naming a term as an example is a hit, and a hit is a prompt to read the line rather than a verdict on it.
|
|
353
355
|
|
|
356
|
+
## Context audit
|
|
357
|
+
|
|
358
|
+
`aitk context audit [path]` reports the structural state of the folders following the index-plus-entry contract, meaning a generated `index.md` beside entries carrying frontmatter. It reads and reports. Fixing what it finds is separate work.
|
|
359
|
+
|
|
360
|
+
```bash
|
|
361
|
+
aitk context audit
|
|
362
|
+
aitk context audit --json
|
|
363
|
+
aitk context audit --citations-only
|
|
364
|
+
aitk context audit --folder context,diagrams
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
| Option | Behavior |
|
|
368
|
+
| ------------------ | ------------------------------------------------------------------------ |
|
|
369
|
+
| `--json` | Add a machine-readable record on stdout, keeping the frame |
|
|
370
|
+
| `--folder <list>` | Comma-separated folder names under `.claude/` (default: the three below) |
|
|
371
|
+
| `--citations-only` | Run the gating citation check alone, printing nothing when it passes |
|
|
372
|
+
|
|
373
|
+
Scope defaults to `context`, `diagrams`, and `wireframes`, and a folder the project does not carry is skipped rather than reported. A domain that outgrew one file and split into `<domain>/` is audited as its own folder, so a split entry measures at the same grain as a flat one.
|
|
374
|
+
|
|
375
|
+
Exit codes are `0` for a clean run, `1` for a refusal, and `2` for an unresolved citation. Only the citation check sets a failing code. Length, depth, table, and index findings print and return `0`, because each is a judgment threshold and failing a push on one would make the check something to route around.
|
|
376
|
+
|
|
377
|
+
### What each check reports
|
|
378
|
+
|
|
379
|
+
Length and depth quote their checkpoints from `.claude/standards/context.md`: roughly 150 lines for an entry, roughly 40 for a run of lines no heading breaks. Depth measures the longest such run rather than everything under one `##`, skips fenced blocks so a markdown example does not read as three headings, and exempts a run whose lines are all list items at one indent. Runs count blank lines, which the standard leaves open, so a hand reader who drops them lands a line or two lower. The report states the convention on every run.
|
|
380
|
+
|
|
381
|
+
The table check reports a catalog that grows a row per shipped thing, not a table count. A fixed comparison table never reflows, so its size costs nothing. A table qualifies at six or more body rows whose first column mostly carries a path, command, or link, which is what separates a catalog from a comparison without reading the prose.
|
|
382
|
+
|
|
383
|
+
Index drift compares an index against its siblings in both directions. An entry the index does not link is invisible to a session choosing what to open, and a linked name resolving to nothing sends one to a path that opens nothing.
|
|
384
|
+
|
|
385
|
+
### The citation gate
|
|
386
|
+
|
|
387
|
+
The citation check resolves every path into an audited folder that appears anywhere in the repository, and it is the half wired into `bun run check`. A stale reference has a silent failure mode: the session opens nothing and carries on.
|
|
388
|
+
|
|
389
|
+
Three exclusions keep it from firing on prose about paths. Fenced blocks are skipped in markdown, which covers a standard displaying a path as an example. Fixture and harness trees are skipped by location, covering sandbox scenarios that describe their own scratch tree, the eval harness naming its target project, and `*.test.ts`. A path into a folder the project does not carry is skipped, so a skill directing a reader to `.claude/wireframes/index.md` stays valid in a project that has wireframes and silent in one that does not.
|
|
390
|
+
|
|
391
|
+
What remains is a sentence naming a hypothetical entry to show the shape of a name, which no syntax separates from a real reference. Append `<!-- audit-ignore-citations -->` to that source line. The marker suppresses citation checking for its own line only.
|
|
392
|
+
|
|
354
393
|
## Runtime catalogs
|
|
355
394
|
|
|
356
395
|
Use these to discover what's available instead of hardcoding names.
|
package/docs/target-projects.md
CHANGED
|
@@ -77,7 +77,7 @@ Run `aitk tooling list --json` and `aitk gov list --json` to see the current cat
|
|
|
77
77
|
`governance`, `standards`, and `wiki` are skippable:
|
|
78
78
|
|
|
79
79
|
- `--skip governance`: leave `.claude/rules/` empty. Standards still install, so `.claude/standards/prose.md` lands with nothing pointing at it and no coding standard loads on a file match. The preview names any `--add` rules the skip drops, and the run prints the `aitk gov install <stack> <path>` command to add rules afterward, carrying those extras so one paste restores what the skip declined.
|
|
80
|
-
- `--skip standards`: leave standards out. The governance rules still reference `.claude/standards/`, so their authority lines resolve to nothing. Toolkit skills are unaffected, since each falls back to the copy in its own plugin root. That fallback now carries runtime behavior rather than reference prose alone, because the
|
|
80
|
+
- `--skip standards`: leave standards out. The governance rules still reference `.claude/standards/`, so their authority lines resolve to nothing. Toolkit skills are unaffected, since each falls back to the copy in its own plugin root. That fallback now carries runtime behavior rather than reference prose alone, because the pre-publish scan and the branch-slug transform are defined in the standards and cited by the skills that run them.
|
|
81
81
|
- `--skip wiki`: skip the `.claude/wiki/` scaffold. A target that already carries a root `wiki/` keeps it, since the verb reports that folder rather than migrating it.
|
|
82
82
|
|
|
83
83
|
## Add a domain later
|
package/package.json
CHANGED
package/scripts/core/verify.sh
CHANGED
|
@@ -148,6 +148,15 @@ main() {
|
|
|
148
148
|
run_check "bash $PROJECT_ROOT/scripts/core/check-plugin-boundary.sh" "Plugin ships toolkit-internal content."
|
|
149
149
|
log_info "Plugin boundary clean"
|
|
150
150
|
|
|
151
|
+
# Only the citation half of the audit gates. Length, depth, table, and index
|
|
152
|
+
# findings are judgment thresholds, and failing a push on one would make the
|
|
153
|
+
# stage something to route around. `bun src/cli.ts` rather than `aitk`, since a
|
|
154
|
+
# globally installed binary resolves to the main checkout no matter which
|
|
155
|
+
# worktree is running.
|
|
156
|
+
log_step "Context citations"
|
|
157
|
+
run_check "cd $PROJECT_ROOT && bun src/cli.ts context audit --citations-only" "A cited context path does not resolve. Run bun src/cli.ts context audit."
|
|
158
|
+
log_info "Context citations resolve"
|
|
159
|
+
|
|
151
160
|
log_step "Plugin manifests"
|
|
152
161
|
if ! command -v claude >/dev/null 2>&1; then
|
|
153
162
|
log_info "Skipped, claude is not installed"
|
|
@@ -21,16 +21,70 @@ resolve_sandbox_dir() {
|
|
|
21
21
|
printf '%s/aitk/sandbox\n' "${XDG_STATE_HOME:-$HOME/.local/state}"
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
# Collapses repeated separators
|
|
25
|
-
# root as `/`. Every comparison below is a
|
|
26
|
-
#
|
|
24
|
+
# Collapses repeated separators, folds `.` and `..` segments, and strips every
|
|
25
|
+
# trailing separator, leaving a bare root as `/`. Every comparison below is a
|
|
26
|
+
# string test, so `//`, `$HOME//`, and `$HOME/../../usr` each read as a path no
|
|
27
|
+
# rule names until this runs, and an unresolved `..` defeats the allowlist and
|
|
28
|
+
# both directions of the repository test at once.
|
|
29
|
+
#
|
|
30
|
+
# The fold is lexical because the guard runs before provisioning creates the
|
|
31
|
+
# tree, which rules out `cd` with `pwd -P` and any resolution needing the path
|
|
32
|
+
# to exist. Nothing here follows a symlink, so a `..` below one resolves against
|
|
33
|
+
# the link's own path rather than its target. A `..` climbing past the root
|
|
34
|
+
# clamps to `/`, matching the kernel, which leaves the allowlist to refuse it
|
|
35
|
+
# under the rule that already covers every root path.
|
|
27
36
|
normalize_sandbox_path() {
|
|
28
37
|
local path="$1"
|
|
29
38
|
|
|
39
|
+
[ -n "$path" ] || return 0
|
|
40
|
+
|
|
30
41
|
while [ "$path" != "${path//\/\//\/}" ]; do
|
|
31
42
|
path="${path//\/\//\/}"
|
|
32
43
|
done
|
|
33
44
|
|
|
45
|
+
local root=""
|
|
46
|
+
if [ "${path#/}" != "$path" ]; then
|
|
47
|
+
root="/"
|
|
48
|
+
path="${path#/}"
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
# `resolved` carries each kept segment behind its own separator, so a pop is
|
|
52
|
+
# one suffix removal and popping an empty stack is the no-op that clamps at
|
|
53
|
+
# the root. `climbed` holds the leading `..` a relative path has no segment to
|
|
54
|
+
# pop against, which dropping would change the directory it names.
|
|
55
|
+
local resolved="" climbed="" segment
|
|
56
|
+
local remaining=4096
|
|
57
|
+
|
|
58
|
+
while [ -n "$path" ] && [ "$remaining" -gt 0 ]; do
|
|
59
|
+
remaining=$((remaining - 1))
|
|
60
|
+
|
|
61
|
+
segment="${path%%/*}"
|
|
62
|
+
if [ "$segment" = "$path" ]; then
|
|
63
|
+
path=""
|
|
64
|
+
else
|
|
65
|
+
path="${path#*/}"
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
case "$segment" in
|
|
69
|
+
"" | .) ;;
|
|
70
|
+
..)
|
|
71
|
+
if [ -n "$resolved" ]; then
|
|
72
|
+
resolved="${resolved%/*}"
|
|
73
|
+
elif [ -z "$root" ]; then
|
|
74
|
+
climbed="$climbed../"
|
|
75
|
+
fi
|
|
76
|
+
;;
|
|
77
|
+
*) resolved="$resolved/$segment" ;;
|
|
78
|
+
esac
|
|
79
|
+
done
|
|
80
|
+
|
|
81
|
+
if [ -n "$root" ]; then
|
|
82
|
+
path="${resolved:-/}"
|
|
83
|
+
else
|
|
84
|
+
path="${climbed}${resolved#/}"
|
|
85
|
+
path="${path:-.}"
|
|
86
|
+
fi
|
|
87
|
+
|
|
34
88
|
while [ "${#path}" -gt 1 ] && [ "${path%/}" != "$path" ]; do
|
|
35
89
|
path="${path%/}"
|
|
36
90
|
done
|
|
@@ -72,13 +126,28 @@ assert_sandbox_dir_safe() {
|
|
|
72
126
|
return 1
|
|
73
127
|
fi
|
|
74
128
|
|
|
129
|
+
# `normalize_sandbox_path` stops at 4096 segments and drops the remainder,
|
|
130
|
+
# which would hand the tests below an ancestor of the path provisioning
|
|
131
|
+
# removes. Refusing past `PATH_MAX` keeps that bound out of reach, since a
|
|
132
|
+
# segment costs at least two bytes and no syscall accepts the string anyway.
|
|
133
|
+
if [ "${#raw}" -gt 4096 ]; then
|
|
134
|
+
printf 'Refusing the sandbox path. It is %s characters, past the longest path any filesystem here accepts.\n' "${#raw}"
|
|
135
|
+
return 1
|
|
136
|
+
fi
|
|
137
|
+
|
|
75
138
|
local dir home temp
|
|
76
139
|
dir="$(normalize_sandbox_path "$raw")"
|
|
77
140
|
home="$(normalize_sandbox_path "${HOME:-/root}")"
|
|
78
141
|
temp="$(normalize_sandbox_path "${TMPDIR:-/tmp}")"
|
|
79
142
|
|
|
143
|
+
# Every message below names `$raw`, which is what the operator set. The tests
|
|
144
|
+
# compare `$dir`, so a path carrying `..` is refused for a location its own
|
|
145
|
+
# spelling does not show.
|
|
146
|
+
local resolution=""
|
|
147
|
+
[ "$dir" = "$raw" ] || resolution=" It resolves to $dir."
|
|
148
|
+
|
|
80
149
|
if ! is_at_or_above "$home" "$dir" && ! is_at_or_above "$temp" "$dir"; then
|
|
81
|
-
printf 'Refusing %s as the sandbox. Provisioning removes the tree first, so the path has to sit under %s or %s
|
|
150
|
+
printf 'Refusing %s as the sandbox. Provisioning removes the tree first, so the path has to sit under %s or %s.%s\n' "$raw" "$home" "$temp" "$resolution"
|
|
82
151
|
return 1
|
|
83
152
|
fi
|
|
84
153
|
|
|
@@ -93,13 +162,13 @@ assert_sandbox_dir_safe() {
|
|
|
93
162
|
main_root="$(normalize_sandbox_path "${main_root:-$root}")"
|
|
94
163
|
|
|
95
164
|
if is_at_or_above "$dir" "$main_root"; then
|
|
96
|
-
printf 'Refusing %s as the sandbox. Provisioning removes the tree first, and that path contains %s
|
|
165
|
+
printf 'Refusing %s as the sandbox. Provisioning removes the tree first, and that path contains %s.%s\n' "$raw" "$main_root" "$resolution"
|
|
97
166
|
return 1
|
|
98
167
|
fi
|
|
99
168
|
|
|
100
169
|
case "$dir" in
|
|
101
170
|
"$main_root"/*)
|
|
102
|
-
printf 'Sandbox at %s sits inside %s, which puts the toolkit CLAUDE.md back on the session ancestor chain
|
|
171
|
+
printf 'Sandbox at %s sits inside %s, which puts the toolkit CLAUDE.md back on the session ancestor chain.%s Point AITK_SANDBOX_DIR outside the repository.\n' "$raw" "$main_root" "$resolution"
|
|
103
172
|
return 1
|
|
104
173
|
;;
|
|
105
174
|
esac
|
package/src/cli.ts
CHANGED
|
@@ -19,6 +19,7 @@ import { register as feedback } from '@/commands/feedback'
|
|
|
19
19
|
import { register as transcripts } from '@/commands/transcripts'
|
|
20
20
|
import { register as tasks } from '@/commands/tasks'
|
|
21
21
|
import { register as comments } from '@/commands/comments'
|
|
22
|
+
import { register as context } from '@/commands/context'
|
|
22
23
|
|
|
23
24
|
const GREY = '\x1b[0;90m'
|
|
24
25
|
const WHITE = '\x1b[1;37m'
|
|
@@ -48,6 +49,7 @@ function showHelp(): void {
|
|
|
48
49
|
`${GREY}│${NC} transcripts <url> ${GREY}# Fetch a YouTube transcript with metadata frontmatter${NC}`,
|
|
49
50
|
`${GREY}│${NC} tasks [cmd] ${GREY}# Task board commands (archive)${NC}`,
|
|
50
51
|
`${GREY}│${NC} comments [cmd] ${GREY}# Measure comment density and trend (scan)${NC}`,
|
|
52
|
+
`${GREY}│${NC} context [cmd] ${GREY}# Report context folder health (audit)${NC}`,
|
|
51
53
|
`${GREY}│${NC}`,
|
|
52
54
|
`${GREY}│${NC} ${WHITE}Sandbox:${NC}`,
|
|
53
55
|
`${GREY}│${NC} aitk sandbox ${GREY}# Interactive scenario picker${NC}`,
|
|
@@ -79,6 +81,7 @@ function showHelp(): void {
|
|
|
79
81
|
`${GREY}│${NC} aitk transcripts https://youtu.be/VIDEO_ID`,
|
|
80
82
|
`${GREY}│${NC} aitk tasks archive --pull-request 673 --json`,
|
|
81
83
|
`${GREY}│${NC} aitk comments scan src --json`,
|
|
84
|
+
`${GREY}│${NC} aitk context audit --json`,
|
|
82
85
|
`${GREY}└${NC}`,
|
|
83
86
|
]
|
|
84
87
|
console.log(lines.join('\n'))
|
|
@@ -115,5 +118,6 @@ feedback(program)
|
|
|
115
118
|
transcripts(program)
|
|
116
119
|
tasks(program)
|
|
117
120
|
comments(program)
|
|
121
|
+
context(program)
|
|
118
122
|
|
|
119
123
|
program.parse()
|