litclaude-ai 0.2.2
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/CHANGELOG.md +155 -0
- package/LICENSE +21 -0
- package/README.md +369 -0
- package/README_ko-KR.md +374 -0
- package/RELEASE_CHECKLIST.md +165 -0
- package/bin/litclaude-ai.js +643 -0
- package/cover.png +0 -0
- package/docs/agents.md +67 -0
- package/docs/hooks.md +134 -0
- package/docs/lsp.md +40 -0
- package/docs/migration.md +209 -0
- package/docs/workflow-compatibility-audit.md +119 -0
- package/generate_cover.py +123 -0
- package/package.json +48 -0
- package/plugins/litclaude/.claude-plugin/plugin.json +25 -0
- package/plugins/litclaude/.lsp.json +13 -0
- package/plugins/litclaude/.mcp.json +9 -0
- package/plugins/litclaude/agents/boulder-executor.md +12 -0
- package/plugins/litclaude/agents/librarian-researcher.md +15 -0
- package/plugins/litclaude/agents/oracle-verifier.md +16 -0
- package/plugins/litclaude/agents/prometheus-planner.md +13 -0
- package/plugins/litclaude/agents/qa-runner.md +16 -0
- package/plugins/litclaude/agents/quality-reviewer.md +17 -0
- package/plugins/litclaude/bin/litclaude-hook.js +110 -0
- package/plugins/litclaude/bin/litclaude-hud.js +271 -0
- package/plugins/litclaude/bin/litclaude-lsp-doctor.js +15 -0
- package/plugins/litclaude/bin/litclaude-mcp.js +70 -0
- package/plugins/litclaude/commands/deep-interview.md +21 -0
- package/plugins/litclaude/commands/dynamic-workflow.md +36 -0
- package/plugins/litclaude/commands/lit-loop.md +40 -0
- package/plugins/litclaude/commands/lit-plan.md +35 -0
- package/plugins/litclaude/commands/litgoal.md +30 -0
- package/plugins/litclaude/commands/review-work.md +35 -0
- package/plugins/litclaude/commands/start-work.md +36 -0
- package/plugins/litclaude/hooks/hooks.json +54 -0
- package/plugins/litclaude/lib/context-pressure.mjs +25 -0
- package/plugins/litclaude/lib/hud-accent-palette.mjs +58 -0
- package/plugins/litclaude/lib/litgoal/cli.mjs +266 -0
- package/plugins/litclaude/lib/litgoal/ledger.mjs +16 -0
- package/plugins/litclaude/lib/litgoal/paths.mjs +7 -0
- package/plugins/litclaude/lib/litgoal/state.mjs +67 -0
- package/plugins/litclaude/lib/mutated-file-paths.mjs +63 -0
- package/plugins/litclaude/lib/start-work-continuation.mjs +99 -0
- package/plugins/litclaude/lib/workflow-check.mjs +83 -0
- package/plugins/litclaude/skills/ai-slop-remover/SKILL.md +142 -0
- package/plugins/litclaude/skills/comment-checker/SKILL.md +55 -0
- package/plugins/litclaude/skills/debugging/SKILL.md +70 -0
- package/plugins/litclaude/skills/debugging/references/methodology/00-setup.md +108 -0
- package/plugins/litclaude/skills/debugging/references/methodology/02-investigate.md +126 -0
- package/plugins/litclaude/skills/debugging/references/methodology/04-oracle-triple.md +106 -0
- package/plugins/litclaude/skills/debugging/references/methodology/05-escalate.md +69 -0
- package/plugins/litclaude/skills/debugging/references/methodology/06-fix.md +116 -0
- package/plugins/litclaude/skills/debugging/references/methodology/08-qa.md +94 -0
- package/plugins/litclaude/skills/debugging/references/methodology/09-cleanup.md +164 -0
- package/plugins/litclaude/skills/debugging/references/methodology/partial-runtime-evidence.md +228 -0
- package/plugins/litclaude/skills/debugging/references/runtimes/bundled-js-binary.md +415 -0
- package/plugins/litclaude/skills/debugging/references/runtimes/go.md +252 -0
- package/plugins/litclaude/skills/debugging/references/runtimes/native-binary.md +484 -0
- package/plugins/litclaude/skills/debugging/references/runtimes/node.md +260 -0
- package/plugins/litclaude/skills/debugging/references/runtimes/python.md +248 -0
- package/plugins/litclaude/skills/debugging/references/runtimes/rust.md +234 -0
- package/plugins/litclaude/skills/debugging/references/tools/ghidra.md +212 -0
- package/plugins/litclaude/skills/debugging/references/tools/playwright-cli.md +194 -0
- package/plugins/litclaude/skills/debugging/references/tools/pwndbg.md +263 -0
- package/plugins/litclaude/skills/debugging/references/tools/pwntools.md +265 -0
- package/plugins/litclaude/skills/deep-interview/SKILL.md +323 -0
- package/plugins/litclaude/skills/deep-interview/scripts/render_progress.py +193 -0
- package/plugins/litclaude/skills/frontend-ui-ux/SKILL.md +62 -0
- package/plugins/litclaude/skills/lit-loop/SKILL.md +144 -0
- package/plugins/litclaude/skills/lit-plan/SKILL.md +125 -0
- package/plugins/litclaude/skills/litgoal/SKILL.md +219 -0
- package/plugins/litclaude/skills/lsp/SKILL.md +63 -0
- package/plugins/litclaude/skills/programming/SKILL.md +106 -0
- package/plugins/litclaude/skills/programming/references/go/README.md +90 -0
- package/plugins/litclaude/skills/programming/references/go/backend-stack.md +641 -0
- package/plugins/litclaude/skills/programming/references/go/bootstrap.md +328 -0
- package/plugins/litclaude/skills/programming/references/go/bubbletea-v2.md +360 -0
- package/plugins/litclaude/skills/programming/references/go/cobra-stack.md +468 -0
- package/plugins/litclaude/skills/programming/references/go/concurrency.md +362 -0
- package/plugins/litclaude/skills/programming/references/go/data-modeling.md +329 -0
- package/plugins/litclaude/skills/programming/references/go/error-handling.md +359 -0
- package/plugins/litclaude/skills/programming/references/go/golangci-strict.md +236 -0
- package/plugins/litclaude/skills/programming/references/go/grpc-connect.md +375 -0
- package/plugins/litclaude/skills/programming/references/go/libraries.md +337 -0
- package/plugins/litclaude/skills/programming/references/go/one-liners.md +202 -0
- package/plugins/litclaude/skills/programming/references/go/sqlc-pgx.md +471 -0
- package/plugins/litclaude/skills/programming/references/go/testing.md +467 -0
- package/plugins/litclaude/skills/programming/references/go/type-patterns.md +298 -0
- package/plugins/litclaude/skills/programming/references/python/README.md +314 -0
- package/plugins/litclaude/skills/programming/references/python/async-anyio.md +442 -0
- package/plugins/litclaude/skills/programming/references/python/data-modeling.md +233 -0
- package/plugins/litclaude/skills/programming/references/python/data-processing.md +133 -0
- package/plugins/litclaude/skills/programming/references/python/error-handling.md +218 -0
- package/plugins/litclaude/skills/programming/references/python/fastapi-stack.md +316 -0
- package/plugins/litclaude/skills/programming/references/python/httpx2-optimization.md +360 -0
- package/plugins/litclaude/skills/programming/references/python/libraries.md +307 -0
- package/plugins/litclaude/skills/programming/references/python/one-liners.md +268 -0
- package/plugins/litclaude/skills/programming/references/python/orjson-stack.md +378 -0
- package/plugins/litclaude/skills/programming/references/python/pydantic-ai.md +285 -0
- package/plugins/litclaude/skills/programming/references/python/pyproject-strict.md +232 -0
- package/plugins/litclaude/skills/programming/references/python/textual-tui.md +201 -0
- package/plugins/litclaude/skills/programming/references/python/type-patterns.md +176 -0
- package/plugins/litclaude/skills/programming/references/rust/README.md +317 -0
- package/plugins/litclaude/skills/programming/references/rust/async-tokio.md +299 -0
- package/plugins/litclaude/skills/programming/references/rust/axum-stack.md +467 -0
- package/plugins/litclaude/skills/programming/references/rust/cargo-strict.md +317 -0
- package/plugins/litclaude/skills/programming/references/rust/clap-stack.md +409 -0
- package/plugins/litclaude/skills/programming/references/rust/concurrency.md +375 -0
- package/plugins/litclaude/skills/programming/references/rust/libraries.md +439 -0
- package/plugins/litclaude/skills/programming/references/rust/one-liners.md +291 -0
- package/plugins/litclaude/skills/programming/references/rust/proptest-insta.md +429 -0
- package/plugins/litclaude/skills/programming/references/rust/type-state.md +354 -0
- package/plugins/litclaude/skills/programming/references/rust/unsafe-discipline.md +250 -0
- package/plugins/litclaude/skills/programming/references/rust/zero-cost-safety.md +527 -0
- package/plugins/litclaude/skills/programming/references/rust-ub/README.md +289 -0
- package/plugins/litclaude/skills/programming/references/rust-ub/miri-sanitizers-loom.md +411 -0
- package/plugins/litclaude/skills/programming/references/rust-ub/ub-taxonomy.md +269 -0
- package/plugins/litclaude/skills/programming/references/typescript/README.md +195 -0
- package/plugins/litclaude/skills/programming/references/typescript/backend-hono.md +672 -0
- package/plugins/litclaude/skills/programming/references/typescript/bootstrap.md +199 -0
- package/plugins/litclaude/skills/programming/references/typescript/data-modeling.md +202 -0
- package/plugins/litclaude/skills/programming/references/typescript/error-handling.md +169 -0
- package/plugins/litclaude/skills/programming/references/typescript/tsconfig-strict.md +152 -0
- package/plugins/litclaude/skills/programming/references/typescript/type-patterns.md +196 -0
- package/plugins/litclaude/skills/programming/scripts/go/check-no-excuse-rules.sh +173 -0
- package/plugins/litclaude/skills/programming/scripts/go/new-project.py +138 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/.editorconfig +13 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/.golangci.yml +95 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/AGENTS.md.tmpl +24 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/README.md.tmpl +12 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/Taskfile.yml +40 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/ci.yml +37 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/config.go +24 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/gitignore +15 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/main.go.tmpl +22 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/run.go +15 -0
- package/plugins/litclaude/skills/programming/scripts/python/check-no-excuse-rules.py +687 -0
- package/plugins/litclaude/skills/programming/scripts/python/new-project.py +172 -0
- package/plugins/litclaude/skills/programming/scripts/python/new-script.py +116 -0
- package/plugins/litclaude/skills/programming/scripts/rust/check-no-excuse-rules.py +296 -0
- package/plugins/litclaude/skills/programming/scripts/rust/check-no-excuse-rules.sh +158 -0
- package/plugins/litclaude/skills/programming/scripts/rust/new-project.py +175 -0
- package/plugins/litclaude/skills/programming/scripts/typescript/check-no-excuse-rules.ts +282 -0
- package/plugins/litclaude/skills/programming/scripts/typescript/new-project.ts +177 -0
- package/plugins/litclaude/skills/refactor/SKILL.md +73 -0
- package/plugins/litclaude/skills/remove-ai-slops/SKILL.md +52 -0
- package/plugins/litclaude/skills/review-work/SKILL.md +331 -0
- package/plugins/litclaude/skills/rules/SKILL.md +66 -0
- package/plugins/litclaude/skills/start-work/SKILL.md +132 -0
- package/scripts/audit-plan-checkboxes.mjs +37 -0
- package/scripts/doctor.mjs +41 -0
- package/scripts/inspect-agent-tools.mjs +27 -0
- package/scripts/postinstall.mjs +50 -0
- package/scripts/qa-claude-plugin-smoke.sh +60 -0
- package/scripts/qa-portable-install.sh +136 -0
- package/scripts/validate-plugin.mjs +72 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: start-work
|
|
3
|
+
description: Execute a checked plan file with LitClaude Boulder state, top-level checkbox discipline, goal/workflow/worktree bootstrap, evidence ledgers, and cleanup receipts.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Start Work
|
|
7
|
+
|
|
8
|
+
Use this skill when the user asks to execute a plan file, especially
|
|
9
|
+
`$start-work plans/<slug>.md`. The plan file and ledger are the source of truth,
|
|
10
|
+
not memory.
|
|
11
|
+
|
|
12
|
+
## Bootstrap
|
|
13
|
+
|
|
14
|
+
Before the first edit:
|
|
15
|
+
|
|
16
|
+
1. Read the requested plan file.
|
|
17
|
+
2. Read `.litclaude/start-work/ledger.jsonl` when it exists.
|
|
18
|
+
3. Inspect `.litclaude/boulder.json` when it exists.
|
|
19
|
+
4. Pick the first unchecked top-level checkbox in `## TODOs` or final
|
|
20
|
+
verification sections. Ignore nested acceptance criteria as task selectors.
|
|
21
|
+
5. Decompose that checkbox into atomic tasks with tests and QA artifacts.
|
|
22
|
+
6. PIN the current objective, stale state to reread, dirty worktree boundaries,
|
|
23
|
+
commit/push approval, publish approval, resume checkpoint, and required
|
|
24
|
+
cleanup receipt paths before editing.
|
|
25
|
+
|
|
26
|
+
## Native Goal + Dynamic Workflow
|
|
27
|
+
|
|
28
|
+
Before the first checkbox, use Claude Code's native goal surface when available:
|
|
29
|
+
|
|
30
|
+
- call `get_goal` if exposed
|
|
31
|
+
- call `create_goal` with the plan objective only when no matching active goal
|
|
32
|
+
exists
|
|
33
|
+
- call `update_goal` only after all top-level checkboxes and final gates are
|
|
34
|
+
complete, or when the plan is genuinely blocked
|
|
35
|
+
- if the user invokes `/goal`, respect it as the native session goal
|
|
36
|
+
- do not auto-type or send `/goal` text from a skill
|
|
37
|
+
|
|
38
|
+
If model-facing goal tools are absent, continue with Boulder/ledger discipline.
|
|
39
|
+
For long plans, ask once for `/goal <plan completion condition>` if that would
|
|
40
|
+
help the user keep the session bounded.
|
|
41
|
+
If goal tools are not exposed, do not print repeated fallback status; just keep
|
|
42
|
+
the plan ledger accurate and continue with verified evidence.
|
|
43
|
+
|
|
44
|
+
For broad, risky, or parallel checkbox waves, use Dynamic workflow orchestration
|
|
45
|
+
and call `Workflow` when exposed. Use Dynamic worktree isolation with
|
|
46
|
+
`EnterWorktree` for isolated model-facing edit lanes when available. When
|
|
47
|
+
only the CLI path is available, use or recommend
|
|
48
|
+
`claude --worktree <short-name> --tmux`.
|
|
49
|
+
|
|
50
|
+
## Subagent Assignment Contract
|
|
51
|
+
|
|
52
|
+
For each decomposed checkbox, child-agent assignments must be executable, not
|
|
53
|
+
context-only. Start each assignment with `TASK:` and include `DELIVERABLE`,
|
|
54
|
+
`SCOPE`, and `VERIFY`. Name exact files or directories, the characterization
|
|
55
|
+
test or RED reproduction, implementation constraints, automated verification,
|
|
56
|
+
Manual-QA channel, adversarial classes, artifact path, and cleanup receipt.
|
|
57
|
+
|
|
58
|
+
Run independent assignments in the background only when file scopes do not
|
|
59
|
+
collide. Use short wait cycles for mailbox updates; a timeout is not a failure
|
|
60
|
+
or approval. If the child returns no deliverable, sends only acknowledgement,
|
|
61
|
+
or reports `BLOCKED:`, record the missing deliverable and issue one targeted
|
|
62
|
+
follow-up before using a smaller fallback assignment. Reviewer fallback must
|
|
63
|
+
keep a reviewer role and is not a generic worker task.
|
|
64
|
+
|
|
65
|
+
## Per-Checkbox Loop
|
|
66
|
+
|
|
67
|
+
For the selected checkbox:
|
|
68
|
+
|
|
69
|
+
1. Re-read the checkbox, acceptance criteria, and prior evidence.
|
|
70
|
+
2. PIN the exact file scope, non-goals, and stale facts that must be refreshed.
|
|
71
|
+
3. Write or update the test first; capture RED when behavior changes.
|
|
72
|
+
4. Implement the smallest change and capture GREEN with the targeted test.
|
|
73
|
+
5. Run broader verification requested by the plan.
|
|
74
|
+
6. Run the plan's Manual-QA scenario and capture the SURFACE artifact.
|
|
75
|
+
7. Run the reviewer gate when the checkbox is broad, risky, shared,
|
|
76
|
+
security-sensitive, or release-facing.
|
|
77
|
+
8. Clean resources and capture cleanup receipt. Cleanup must be bounded with a
|
|
78
|
+
timeout or kill path for tmux sessions, servers, ports, browser contexts,
|
|
79
|
+
temp directories, and child processes.
|
|
80
|
+
9. Mark the top-level checkbox complete only after evidence exists.
|
|
81
|
+
10. Append a `task-completed` line to `.litclaude/start-work/ledger.jsonl`.
|
|
82
|
+
|
|
83
|
+
Do not mark every checkbox at the end in a batch. Progress is durable and
|
|
84
|
+
incremental.
|
|
85
|
+
|
|
86
|
+
Use `PIN -> RED -> GREEN -> VERIFY -> SURFACE -> REVIEW -> CLEAN -> RECORD` as
|
|
87
|
+
the execution shorthand. If any step is missing, leave the checkbox open.
|
|
88
|
+
|
|
89
|
+
## Evidence Standards
|
|
90
|
+
|
|
91
|
+
Every completed checkbox needs:
|
|
92
|
+
|
|
93
|
+
- automated test command and result
|
|
94
|
+
- manual QA artifact path
|
|
95
|
+
- cleanup receipt
|
|
96
|
+
- reviewer result or a recorded reason the reviewer gate was not required
|
|
97
|
+
- changed files or commit hash when applicable
|
|
98
|
+
- any skipped adversarial classes with reason
|
|
99
|
+
|
|
100
|
+
`--dry-run`, "looks right", or a worker's self-report is insufficient.
|
|
101
|
+
|
|
102
|
+
## Failure Handling
|
|
103
|
+
|
|
104
|
+
If a subtask fails, retry the same task with the exact error, diagnosis, and
|
|
105
|
+
fix instruction. After repeated identical failures, stop and surface the
|
|
106
|
+
blocker with artifacts. Do not start fresh and lose the failure context.
|
|
107
|
+
|
|
108
|
+
On resume after cancel, compaction, terminal restart, or repeated interruption,
|
|
109
|
+
reread the plan, ledger, `.litclaude/boulder.json`, and `git status --short` before
|
|
110
|
+
acting. Treat prior green output as stale unless the artifact path still exists
|
|
111
|
+
and includes the command plus STATUS line. Never revert dirty worktree changes
|
|
112
|
+
you did not make.
|
|
113
|
+
|
|
114
|
+
Use `litclaude-ai start-work-next --json` when you need a compact continuation
|
|
115
|
+
directive from the current `.litclaude/boulder.json` state. It prints the active plan,
|
|
116
|
+
ledger, worktree when known, and the next unchecked top-level checkbox without
|
|
117
|
+
mutating state. If Claude Code later exposes a stable Stop/SubagentStop plugin
|
|
118
|
+
hook schema for this package shape, wire the same helper there; until then the
|
|
119
|
+
CLI helper is the supported continuation surface.
|
|
120
|
+
|
|
121
|
+
## Finalization
|
|
122
|
+
|
|
123
|
+
When all top-level checkboxes are complete, run the plan's final verification
|
|
124
|
+
wave, update the plan file, update the ledger, commit/push if requested, and
|
|
125
|
+
leave a handoff if the session stops at a checkpoint.
|
|
126
|
+
|
|
127
|
+
## Stop rules
|
|
128
|
+
|
|
129
|
+
Stop before commit/push or publish unless the user approved it for this run.
|
|
130
|
+
Stop when the plan is malformed, missing acceptance criteria, missing Manual-QA
|
|
131
|
+
channels, missing cleanup receipts, or contradicts current repository state.
|
|
132
|
+
Record the blocker and the next executable command instead of guessing.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { readFile } from "node:fs/promises";
|
|
4
|
+
|
|
5
|
+
const planPath = process.argv[2];
|
|
6
|
+
|
|
7
|
+
if (!planPath) {
|
|
8
|
+
process.stderr.write("usage: audit-plan-checkboxes.mjs <plan.md>\n");
|
|
9
|
+
process.exit(64);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const text = await readFile(planPath, "utf8");
|
|
13
|
+
const taskPattern = /^- \[[ x]\] (?:\d+\.|F\d\.) .+$/gm;
|
|
14
|
+
const tasks = [...text.matchAll(taskPattern)];
|
|
15
|
+
|
|
16
|
+
assert.ok(tasks.some((task) => /^- \[[ x]\] \d+\./.test(task[0])), "expected implementation task checkboxes");
|
|
17
|
+
assert.ok(tasks.some((task) => /^- \[[ x]\] F\d\./.test(task[0])), "expected final verification checkboxes");
|
|
18
|
+
|
|
19
|
+
for (let index = 0; index < tasks.length; index += 1) {
|
|
20
|
+
const start = tasks[index].index;
|
|
21
|
+
const end = tasks[index + 1]?.index ?? text.length;
|
|
22
|
+
const block = text.slice(start, end);
|
|
23
|
+
const title = tasks[index][0];
|
|
24
|
+
|
|
25
|
+
if (/^- \[[ x]\] F\d\./.test(title)) {
|
|
26
|
+
assert.match(block, /(?:Commands?|Tool):/, `${title} missing command or tool`);
|
|
27
|
+
assert.match(block, /(?:Expected|Pass):/, `${title} missing expected result`);
|
|
28
|
+
} else {
|
|
29
|
+
assert.match(title, /^- \[x\]/, `${title} is not complete`);
|
|
30
|
+
assert.match(block, /\*\*References\*\*:/, `${title} missing references`);
|
|
31
|
+
assert.match(block, /\*\*Acceptance Criteria\*\*:/, `${title} missing acceptance criteria`);
|
|
32
|
+
assert.match(block, /\*\*QA Scenarios\*\*:/, `${title} missing QA scenarios`);
|
|
33
|
+
assert.match(block, /\*\*Commit\*\*:/, `${title} missing commit metadata`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
process.stdout.write(`PLAN_AUDIT_PASS: ${tasks.length} checked items complete\n`);
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
4
|
+
import { join, resolve } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
|
|
7
|
+
const root = resolve(fileURLToPath(new URL("..", import.meta.url)));
|
|
8
|
+
|
|
9
|
+
function run(label, command, args) {
|
|
10
|
+
const result = spawnSync(command, args, {
|
|
11
|
+
cwd: root,
|
|
12
|
+
encoding: "utf8",
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
process.stdout.write(`\n## ${label}\n`);
|
|
16
|
+
if (result.stdout) process.stdout.write(result.stdout);
|
|
17
|
+
if (result.stderr) process.stdout.write(result.stderr);
|
|
18
|
+
|
|
19
|
+
if (result.status !== 0) {
|
|
20
|
+
process.stderr.write(`${label} failed with status ${result.status}\n`);
|
|
21
|
+
process.exit(result.status ?? 1);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
run("CLI dry-run", process.execPath, ["bin/litclaude-ai.js", "--dry-run", "doctor"]);
|
|
26
|
+
run("Plugin validation", process.execPath, ["scripts/validate-plugin.mjs"]);
|
|
27
|
+
run("LSP doctor", process.execPath, ["plugins/litclaude/bin/litclaude-lsp-doctor.js"]);
|
|
28
|
+
|
|
29
|
+
for (const file of [
|
|
30
|
+
"plugins/litclaude/lib/litgoal/paths.mjs",
|
|
31
|
+
"plugins/litclaude/lib/litgoal/state.mjs",
|
|
32
|
+
"plugins/litclaude/lib/litgoal/ledger.mjs",
|
|
33
|
+
"plugins/litclaude/lib/litgoal/cli.mjs",
|
|
34
|
+
]) {
|
|
35
|
+
if (!existsSync(join(root, file))) {
|
|
36
|
+
process.stderr.write(`Runtime payload missing: ${file}\n`);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
process.stdout.write("\nLITGOAL_RUNTIME_PASS\n");
|
|
41
|
+
process.stdout.write("\nDOCTOR_PASS\n");
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
|
|
5
|
+
const path = process.argv[2];
|
|
6
|
+
|
|
7
|
+
if (!path) {
|
|
8
|
+
console.error("Usage: inspect-agent-tools.mjs <agent-file>");
|
|
9
|
+
process.exit(64);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const text = readFileSync(path, "utf8");
|
|
13
|
+
const match = /^---\n([\s\S]*?)\n---/u.exec(text);
|
|
14
|
+
|
|
15
|
+
if (!match) {
|
|
16
|
+
console.error("agent frontmatter not found");
|
|
17
|
+
process.exit(65);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const toolsLine = match[1].split("\n").find((line) => line.startsWith("tools:")) ?? "tools:";
|
|
21
|
+
const tools = toolsLine
|
|
22
|
+
.replace("tools:", "")
|
|
23
|
+
.split(",")
|
|
24
|
+
.map((tool) => tool.trim())
|
|
25
|
+
.filter(Boolean);
|
|
26
|
+
|
|
27
|
+
console.log(JSON.stringify({ path, tools }, null, 2));
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { spawnSync } from "node:child_process";
|
|
4
|
+
import { existsSync } from "node:fs";
|
|
5
|
+
import { dirname, join, resolve } from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
|
|
8
|
+
const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
9
|
+
|
|
10
|
+
const skip = (reason) => {
|
|
11
|
+
process.stdout.write(`LitClaude postinstall skipped: ${reason}\n`);
|
|
12
|
+
process.stdout.write("Run `litclaude install` to register the Claude Code plugin and HUD.\n");
|
|
13
|
+
process.exit(0);
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
if (process.env.LITCLAUDE_AUTO_INSTALL === "0" || process.env.LITCLAUDE_POSTINSTALL_SKIP === "1") {
|
|
17
|
+
skip("disabled by environment");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const forced = process.env.LITCLAUDE_AUTO_INSTALL === "1";
|
|
21
|
+
if (process.env.CI && !forced) {
|
|
22
|
+
skip("CI environment");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (existsSync(join(root, ".git")) && !forced) {
|
|
26
|
+
skip("source checkout");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (process.env.npm_config_global !== "true" && process.env.npm_config_location !== "global" && !forced) {
|
|
30
|
+
skip("non-global package install");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const result = spawnSync(process.execPath, [join(root, "bin", "litclaude-ai.js"), "install"], {
|
|
34
|
+
cwd: root,
|
|
35
|
+
encoding: "utf8",
|
|
36
|
+
env: {
|
|
37
|
+
...process.env,
|
|
38
|
+
LITCLAUDE_POSTINSTALL: "1",
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
if (result.status === 0) {
|
|
43
|
+
if (result.stdout) process.stdout.write(result.stdout);
|
|
44
|
+
process.exit(0);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
process.stdout.write("LitClaude postinstall warning: automatic Claude Code plugin/HUD setup did not complete.\n");
|
|
48
|
+
if (result.stderr) process.stdout.write(result.stderr);
|
|
49
|
+
process.stdout.write("Run `litclaude install` manually after npm finishes.\n");
|
|
50
|
+
process.exit(0);
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -u
|
|
3
|
+
|
|
4
|
+
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
5
|
+
EVIDENCE="$ROOT/evidence/task-9-claude-smoke.txt"
|
|
6
|
+
SESSION="litclaude-plugin-smoke-inner-$$"
|
|
7
|
+
|
|
8
|
+
mkdir -p "$ROOT/evidence"
|
|
9
|
+
: > "$EVIDENCE"
|
|
10
|
+
|
|
11
|
+
cleanup() {
|
|
12
|
+
if command -v tmux >/dev/null 2>&1; then
|
|
13
|
+
tmux kill-session -t "$SESSION" >/dev/null 2>&1 || true
|
|
14
|
+
fi
|
|
15
|
+
}
|
|
16
|
+
trap cleanup EXIT INT TERM
|
|
17
|
+
|
|
18
|
+
{
|
|
19
|
+
echo "LitClaude Claude Code smoke"
|
|
20
|
+
echo "ROOT: $ROOT"
|
|
21
|
+
date
|
|
22
|
+
} >> "$EVIDENCE"
|
|
23
|
+
|
|
24
|
+
if ! command -v claude >/dev/null 2>&1; then
|
|
25
|
+
{
|
|
26
|
+
echo "CONTROLLED_SKIP: claude executable unavailable"
|
|
27
|
+
echo "CLEANUP: no tmux session started"
|
|
28
|
+
} >> "$EVIDENCE"
|
|
29
|
+
echo "CONTROLLED_SKIP: claude executable unavailable"
|
|
30
|
+
exit 0
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
CLAUDE_VERSION="$(claude --version 2>&1 || true)"
|
|
34
|
+
echo "CLAUDE_VERSION: $CLAUDE_VERSION" >> "$EVIDENCE"
|
|
35
|
+
|
|
36
|
+
if ! command -v tmux >/dev/null 2>&1; then
|
|
37
|
+
HELP_OUTPUT="$(claude --plugin-dir "$ROOT/plugins/litclaude" --help 2>&1 || true)"
|
|
38
|
+
{
|
|
39
|
+
echo "CONTROLLED_SKIP: tmux unavailable"
|
|
40
|
+
echo "$HELP_OUTPUT" | sed -n '1,40p'
|
|
41
|
+
echo "CLEANUP: no tmux session started"
|
|
42
|
+
} >> "$EVIDENCE"
|
|
43
|
+
echo "CONTROLLED_SKIP: tmux unavailable"
|
|
44
|
+
exit 0
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
tmux new-session -d -s "$SESSION" "cd '$ROOT' && claude --plugin-dir ./plugins/litclaude --help; printf '\nINNER_STATUS:%s\n' \"\$?\"; sleep 5"
|
|
48
|
+
sleep 1
|
|
49
|
+
tmux capture-pane -pt "$SESSION" -S -200 >> "$EVIDENCE" 2>&1 || true
|
|
50
|
+
tmux kill-session -t "$SESSION" >/dev/null 2>&1 || true
|
|
51
|
+
|
|
52
|
+
if rg -q "INNER_STATUS:0|Usage|Claude Code" "$EVIDENCE"; then
|
|
53
|
+
echo "SMOKE_PASS: claude --plugin-dir accepted local LitClaude plugin path" >> "$EVIDENCE"
|
|
54
|
+
echo "CLEANUP: $SESSION killed" >> "$EVIDENCE"
|
|
55
|
+
echo "SMOKE_PASS: claude --plugin-dir accepted local LitClaude plugin path"
|
|
56
|
+
else
|
|
57
|
+
echo "CONTROLLED_SKIP: claude help did not expose a stable plugin-dir smoke result" >> "$EVIDENCE"
|
|
58
|
+
echo "CLEANUP: $SESSION killed" >> "$EVIDENCE"
|
|
59
|
+
echo "CONTROLLED_SKIP: claude help did not expose a stable plugin-dir smoke result"
|
|
60
|
+
fi
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -u
|
|
3
|
+
|
|
4
|
+
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
5
|
+
EVIDENCE="$ROOT/evidence/portable-qa-install.txt"
|
|
6
|
+
TMP_HOME=""
|
|
7
|
+
TMP_CLAUDE_HOME=""
|
|
8
|
+
|
|
9
|
+
mkdir -p "$ROOT/evidence"
|
|
10
|
+
: > "$EVIDENCE"
|
|
11
|
+
|
|
12
|
+
cleanup() {
|
|
13
|
+
if [ -n "$TMP_HOME" ]; then
|
|
14
|
+
rm -rf "$TMP_HOME"
|
|
15
|
+
fi
|
|
16
|
+
if [ -n "$TMP_CLAUDE_HOME" ]; then
|
|
17
|
+
rm -rf "$TMP_CLAUDE_HOME"
|
|
18
|
+
fi
|
|
19
|
+
}
|
|
20
|
+
trap cleanup EXIT INT TERM
|
|
21
|
+
|
|
22
|
+
{
|
|
23
|
+
echo "LitClaude portable install QA"
|
|
24
|
+
echo "ROOT: $ROOT"
|
|
25
|
+
date
|
|
26
|
+
} >> "$EVIDENCE"
|
|
27
|
+
|
|
28
|
+
TMP_HOME="$(mktemp -d "${TMPDIR:-/tmp/}litclaude-portable-qa.XXXXXX")"
|
|
29
|
+
TMP_CLAUDE_HOME="$(mktemp -d "${TMPDIR:-/tmp/}litclaude-claude-qa.XXXXXX")"
|
|
30
|
+
export LITCLAUDE_HOME="$TMP_HOME"
|
|
31
|
+
export CLAUDE_CONFIG_DIR="$TMP_CLAUDE_HOME"
|
|
32
|
+
|
|
33
|
+
run_step() {
|
|
34
|
+
local label="$1"
|
|
35
|
+
shift
|
|
36
|
+
local status
|
|
37
|
+
{
|
|
38
|
+
echo
|
|
39
|
+
echo "## $label"
|
|
40
|
+
"$@"
|
|
41
|
+
status="$?"
|
|
42
|
+
echo "STATUS:$status"
|
|
43
|
+
} >> "$EVIDENCE" 2>&1
|
|
44
|
+
if [ "$status" != "0" ]; then
|
|
45
|
+
echo "${label}_FAIL"
|
|
46
|
+
exit "$status"
|
|
47
|
+
fi
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
run_step INSTALL node "$ROOT/bin/litclaude-ai.js" install
|
|
51
|
+
|
|
52
|
+
PLUGIN_PATH="$(node "$ROOT/bin/litclaude-ai.js" path)"
|
|
53
|
+
{
|
|
54
|
+
echo
|
|
55
|
+
echo "## PATH"
|
|
56
|
+
echo "$PLUGIN_PATH"
|
|
57
|
+
} >> "$EVIDENCE"
|
|
58
|
+
|
|
59
|
+
if [ ! -f "$PLUGIN_PATH/.claude-plugin/plugin.json" ]; then
|
|
60
|
+
echo "INSTALL_FAIL: plugin manifest missing" >> "$EVIDENCE"
|
|
61
|
+
echo "INSTALL_FAIL"
|
|
62
|
+
exit 1
|
|
63
|
+
fi
|
|
64
|
+
for runtime_file in \
|
|
65
|
+
"$PLUGIN_PATH/lib/litgoal/paths.mjs" \
|
|
66
|
+
"$PLUGIN_PATH/lib/litgoal/state.mjs" \
|
|
67
|
+
"$PLUGIN_PATH/lib/litgoal/ledger.mjs" \
|
|
68
|
+
"$PLUGIN_PATH/lib/litgoal/cli.mjs"
|
|
69
|
+
do
|
|
70
|
+
if [ ! -f "$runtime_file" ]; then
|
|
71
|
+
echo "INSTALL_FAIL: litgoal runtime missing: $runtime_file" >> "$EVIDENCE"
|
|
72
|
+
echo "INSTALL_FAIL"
|
|
73
|
+
exit 1
|
|
74
|
+
fi
|
|
75
|
+
done
|
|
76
|
+
echo "LITGOAL_RUNTIME_PASS" >> "$EVIDENCE"
|
|
77
|
+
if [ ! -f "$TMP_CLAUDE_HOME/plugins/installed_plugins.json" ]; then
|
|
78
|
+
echo "INSTALL_FAIL: Claude plugin registry missing" >> "$EVIDENCE"
|
|
79
|
+
echo "INSTALL_FAIL"
|
|
80
|
+
exit 1
|
|
81
|
+
fi
|
|
82
|
+
if [ ! -f "$TMP_CLAUDE_HOME/settings.json" ]; then
|
|
83
|
+
echo "INSTALL_FAIL: Claude settings missing" >> "$EVIDENCE"
|
|
84
|
+
echo "INSTALL_FAIL"
|
|
85
|
+
exit 1
|
|
86
|
+
fi
|
|
87
|
+
if [ ! -f "$TMP_CLAUDE_HOME/plugins/known_marketplaces.json" ]; then
|
|
88
|
+
echo "INSTALL_FAIL: Claude known marketplaces missing" >> "$EVIDENCE"
|
|
89
|
+
echo "INSTALL_FAIL"
|
|
90
|
+
exit 1
|
|
91
|
+
fi
|
|
92
|
+
if [ ! -f "$TMP_HOME/marketplaces/litclaude-ai/.claude-plugin/marketplace.json" ]; then
|
|
93
|
+
echo "INSTALL_FAIL: LitClaude local marketplace missing" >> "$EVIDENCE"
|
|
94
|
+
echo "INSTALL_FAIL"
|
|
95
|
+
exit 1
|
|
96
|
+
fi
|
|
97
|
+
node -e 'const fs = require("fs"); const p = process.argv[1]; const registry = JSON.parse(fs.readFileSync(`${p}/plugins/installed_plugins.json`, "utf8")); const entry = registry.plugins["litclaude@litclaude-ai"]?.[0]; if (!entry || entry.scope !== "user" || !entry.installPath.includes("/plugins/cache/litclaude-ai/litclaude/")) process.exit(1);' "$TMP_CLAUDE_HOME"
|
|
98
|
+
node -e 'const fs = require("fs"); const p = process.argv[1]; const h = process.argv[2]; const settings = JSON.parse(fs.readFileSync(`${p}/settings.json`, "utf8")); const market = settings.extraKnownMarketplaces?.["litclaude-ai"]; if (settings.enabledPlugins?.["litclaude@litclaude-ai"] !== true || market?.source?.source !== "directory" || market.source.path !== `${h}/marketplaces/litclaude-ai`) process.exit(1);' "$TMP_CLAUDE_HOME" "$TMP_HOME"
|
|
99
|
+
node -e 'const fs = require("fs"); const p = process.argv[1]; const h = process.argv[2]; const known = JSON.parse(fs.readFileSync(`${p}/plugins/known_marketplaces.json`, "utf8")); const market = known["litclaude-ai"]; if (market?.source?.source !== "directory" || market.installLocation !== `${h}/marketplaces/litclaude-ai`) process.exit(1);' "$TMP_CLAUDE_HOME" "$TMP_HOME"
|
|
100
|
+
echo "INSTALL_PASS" >> "$EVIDENCE"
|
|
101
|
+
|
|
102
|
+
run_step DOCTOR node "$ROOT/bin/litclaude-ai.js" doctor
|
|
103
|
+
echo "DOCTOR_PASS" >> "$EVIDENCE"
|
|
104
|
+
|
|
105
|
+
if command -v claude >/dev/null 2>&1; then
|
|
106
|
+
run_step CLAUDE_PLUGIN_DETAILS claude plugin details litclaude@litclaude-ai
|
|
107
|
+
if ! grep -Eq "Skills \\([0-9]+\\)" "$EVIDENCE" || ! grep -q "lit-loop" "$EVIDENCE" || ! grep -q "lit-plan" "$EVIDENCE" || ! grep -q "Hooks (4)" "$EVIDENCE"; then
|
|
108
|
+
echo "CLAUDE_PLUGIN_DETAILS_FAIL: expected LitClaude skills/hooks inventory" >> "$EVIDENCE"
|
|
109
|
+
echo "CLAUDE_PLUGIN_DETAILS_FAIL"
|
|
110
|
+
exit 1
|
|
111
|
+
fi
|
|
112
|
+
else
|
|
113
|
+
echo "CLAUDE_PLUGIN_DETAILS_SKIP: claude executable not found" >> "$EVIDENCE"
|
|
114
|
+
fi
|
|
115
|
+
|
|
116
|
+
run_step DRY_RUN_UNINSTALL node "$ROOT/bin/litclaude-ai.js" --dry-run uninstall
|
|
117
|
+
if [ ! -d "$PLUGIN_PATH" ]; then
|
|
118
|
+
echo "DRY_RUN_UNINSTALL_FAIL: plugin removed during dry-run" >> "$EVIDENCE"
|
|
119
|
+
exit 1
|
|
120
|
+
fi
|
|
121
|
+
|
|
122
|
+
run_step UNINSTALL node "$ROOT/bin/litclaude-ai.js" uninstall
|
|
123
|
+
if [ -e "$TMP_HOME/current" ] || [ -e "$TMP_HOME/litclaude-ai" ] || [ -e "$TMP_CLAUDE_HOME/plugins/cache/litclaude-ai" ]; then
|
|
124
|
+
echo "UNINSTALL_FAIL: managed state remains" >> "$EVIDENCE"
|
|
125
|
+
echo "UNINSTALL_FAIL"
|
|
126
|
+
exit 1
|
|
127
|
+
fi
|
|
128
|
+
node -e 'const fs = require("fs"); const p = process.argv[1]; const settings = JSON.parse(fs.readFileSync(`${p}/settings.json`, "utf8")); if (settings.enabledPlugins?.["litclaude@litclaude-ai"] !== undefined) process.exit(1);' "$TMP_CLAUDE_HOME"
|
|
129
|
+
node -e 'const fs = require("fs"); const p = process.argv[1]; const settings = JSON.parse(fs.readFileSync(`${p}/settings.json`, "utf8")); const known = JSON.parse(fs.readFileSync(`${p}/plugins/known_marketplaces.json`, "utf8")); if (settings.extraKnownMarketplaces?.["litclaude-ai"] !== undefined || known["litclaude-ai"] !== undefined) process.exit(1);' "$TMP_CLAUDE_HOME"
|
|
130
|
+
echo "UNINSTALL_PASS" >> "$EVIDENCE"
|
|
131
|
+
|
|
132
|
+
cleanup
|
|
133
|
+
TMP_HOME=""
|
|
134
|
+
TMP_CLAUDE_HOME=""
|
|
135
|
+
echo "CLEANUP: removed portable QA temp home" >> "$EVIDENCE"
|
|
136
|
+
echo "PORTABLE_QA_PASS"
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { access, readdir, readFile } from "node:fs/promises";
|
|
4
|
+
import { constants } from "node:fs";
|
|
5
|
+
import { join, resolve } from "node:path";
|
|
6
|
+
import { spawnSync } from "node:child_process";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
|
|
9
|
+
const root = resolve(fileURLToPath(new URL("..", import.meta.url)));
|
|
10
|
+
const pluginRoot = join(root, "plugins", "litclaude");
|
|
11
|
+
|
|
12
|
+
async function readJson(path) {
|
|
13
|
+
return JSON.parse(await readFile(path, "utf8"));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async function exists(path) {
|
|
17
|
+
await access(path, constants.R_OK);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async function main() {
|
|
21
|
+
const manifest = await readJson(join(pluginRoot, ".claude-plugin", "plugin.json"));
|
|
22
|
+
assert.equal(manifest.name, "litclaude");
|
|
23
|
+
assert.equal(manifest.skills, "./skills");
|
|
24
|
+
assert.deepEqual(manifest.agents, [
|
|
25
|
+
"./agents/boulder-executor.md",
|
|
26
|
+
"./agents/librarian-researcher.md",
|
|
27
|
+
"./agents/oracle-verifier.md",
|
|
28
|
+
"./agents/prometheus-planner.md",
|
|
29
|
+
"./agents/qa-runner.md",
|
|
30
|
+
"./agents/quality-reviewer.md",
|
|
31
|
+
]);
|
|
32
|
+
assert.equal("hooks" in manifest, false);
|
|
33
|
+
assert.equal(manifest.mcpServers, "./.mcp.json");
|
|
34
|
+
assert.equal(manifest.lspServers, "./.lsp.json");
|
|
35
|
+
|
|
36
|
+
await exists(join(pluginRoot, ".mcp.json"));
|
|
37
|
+
await exists(join(pluginRoot, ".lsp.json"));
|
|
38
|
+
await exists(join(pluginRoot, "bin", "litclaude-hook.js"));
|
|
39
|
+
await exists(join(pluginRoot, "bin", "litclaude-lsp-doctor.js"));
|
|
40
|
+
await exists(join(pluginRoot, "bin", "litclaude-mcp.js"));
|
|
41
|
+
|
|
42
|
+
const hooks = await readJson(join(pluginRoot, "hooks", "hooks.json"));
|
|
43
|
+
for (const event of ["SessionStart", "UserPromptSubmit", "PostToolUse", "PostCompact"]) {
|
|
44
|
+
assert.ok(hooks.hooks[event], `missing hook event ${event}`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const skills = await readdir(join(pluginRoot, "skills"));
|
|
48
|
+
const agents = await readdir(join(pluginRoot, "agents"));
|
|
49
|
+
assert.ok(skills.length >= 7, "expected at least seven skills");
|
|
50
|
+
assert.ok(agents.length >= 6, "expected at least six agents");
|
|
51
|
+
|
|
52
|
+
const claude = spawnSync("claude", ["--version"], { encoding: "utf8" });
|
|
53
|
+
if (claude.status === 0) {
|
|
54
|
+
process.stdout.write(`CLAUDE_VERSION: ${claude.stdout.trim() || claude.stderr.trim()}\n`);
|
|
55
|
+
const validation = spawnSync("claude", ["plugin", "validate", "./plugins/litclaude"], {
|
|
56
|
+
cwd: root,
|
|
57
|
+
encoding: "utf8",
|
|
58
|
+
});
|
|
59
|
+
if (validation.stdout) process.stdout.write(validation.stdout);
|
|
60
|
+
if (validation.stderr) process.stdout.write(validation.stderr);
|
|
61
|
+
assert.equal(validation.status, 0, "claude plugin validate must pass");
|
|
62
|
+
} else {
|
|
63
|
+
process.stdout.write("CONTROLLED_SKIP: claude executable unavailable for version probe\n");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
process.stdout.write("VALIDATE_PLUGIN_PASS\n");
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
main().catch((error) => {
|
|
70
|
+
process.stderr.write(`VALIDATE_PLUGIN_FAIL: ${error.message}\n`);
|
|
71
|
+
process.exit(1);
|
|
72
|
+
});
|