claude-dev-env 7.7.0 → 8.0.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/.agents/agents/clean-coder.md +5 -6
- package/.agents/agents/issue-tracker.md +1 -1
- package/.agents/agents/pr-description-writer.md +1 -1
- package/.agents/agents/skill-writer-agent.md +1 -12
- package/.agents/agents/test_agent_frontmatter.py +2 -19
- package/.agents/skills/e-code-review/reference/fix.md +4 -2
- package/.agents/skills/e-code-review/reference/loop.md +4 -2
- package/.agents/skills/issue-tracker/SKILL.md +1 -1
- package/.agents/skills/orchestrator/SKILL.md +4 -5
- package/.agents/skills/pull-request/SKILL.md +8 -7
- package/.agents/skills/pull-request/reference/publication-tasks.md +1 -1
- package/.agents/skills/team-advisor/SKILL.md +2 -3
- package/_shared/advisor/advisor-protocol.md +0 -1
- package/_shared/advisor/reference/lifecycle.md +0 -1
- package/_shared/advisor/reference/warm-up.md +1 -2
- package/audit-rubrics/prompts/category-e-dead-code.md +3 -3
- package/bin/install-constants.mjs +0 -5
- package/bin/install-pstack-plugin.mjs +192 -0
- package/bin/install-pstack-plugin.test.mjs +181 -0
- package/bin/install.agents-home.test.mjs +0 -4
- package/bin/install.codex-rules.test.mjs +6 -0
- package/bin/install.cursor-rules.test.mjs +3 -253
- package/bin/install.mjs +55 -160
- package/bin/install.test.mjs +110 -149
- package/bin/prune-retired-pstack-store.mjs +83 -0
- package/bin/prune-retired-pstack-store.test.mjs +105 -0
- package/bin/prune-session-continuity.mjs +57 -0
- package/bin/prune-session-continuity.test.mjs +70 -0
- package/bin/resolve-install-root.mjs +2 -0
- package/bin/resolve-install-root.test.mjs +69 -0
- package/bin/subagent-model-policy.test.mjs +56 -19
- package/docs/CODE_RULES.md +1 -1
- package/docs/agent-spawn-protocol.md +1 -1
- package/docs/codex-compatibility.md +4 -16
- package/docs/nas-ssh-invocation.md +1 -1
- package/docs/references/prose-style-enforcement.md +2 -2
- package/docs/subagent-model-routing.md +21 -2
- package/hooks/HOOK_SPECS_PROMPT_WORKFLOW.md +1 -3
- package/hooks/blocking/code_rules_constants_config.py +1 -108
- package/hooks/blocking/code_rules_enforcer.py +9 -5
- package/hooks/blocking/code_rules_test_layout.py +6 -7
- package/hooks/blocking/test_code_rules_constants_config.py +13 -0
- package/hooks/blocking/test_code_rules_enforcer_cap_meta.py +0 -1
- package/hooks/blocking/test_code_rules_enforcer_dispatch_wiring.py +15 -0
- package/hooks/blocking/test_code_rules_enforcer_split_constants_config.py +0 -60
- package/hooks/blocking/test_context_control_policy_files.py +0 -11
- package/hooks/hooks_constants/blocking_check_limits.py +1 -1
- package/hooks/hooks_constants/code_rules_enforcer_constants.py +0 -1
- package/hooks/hooks_constants/setup_project_paths_constants.py +1 -2
- package/hooks/routing/subagent_model_routing.test.mjs +65 -28
- package/hooks/session/test_untracked_repo_detector.py +1 -1
- package/hooks/validators/code_quality_checks.py +15 -2
- package/hooks/validators/test_code_quality_checks.py +22 -0
- package/package.json +1 -2
- package/rules/ci-owns-the-gate.md +58 -0
- package/rules/code-standards.md +1 -2
- package/rules/docstring-prose-matches-implementation.md +2 -2
- package/rules/failure-blast-radius.md +1 -1
- package/rules/git-workflow.md +23 -11
- package/rules/orphan-css-class.md +1 -1
- package/rules/paired-test-coverage.md +1 -1
- package/rules/retired-hook-prose.md +1 -1
- package/rules/subagent-model-policy.json +1 -1
- package/rules/verify-before-asking.md +1 -1
- package/rules/windows-filesystem-safe.md +1 -1
- package/rules/workflow-substitution-slots.md +1 -1
- package/scripts/Sync-RepoMain.ps1 +71 -6
- package/scripts/policy_lint/adapter_detectors.py +116 -10
- package/scripts/policy_lint/config/approved_test_pairs.py +12 -0
- package/scripts/policy_lint/config/constants.py +3 -0
- package/scripts/tests/Sync-RepoMain.Tests.ps1 +108 -0
- package/scripts/tests/test_policy_lint_rules_validator_baseline.py +126 -0
- package/.agents/skills/session-continuity/AGENTS.md +0 -1
- package/.agents/skills/session-continuity/CLAUDE.md +0 -1
- package/.agents/skills/session-continuity/README.md +0 -223
- package/.agents/skills/session-continuity/SKILL.md +0 -125
- package/.agents/skills/session-continuity/continuity.mjs +0 -445
- package/.agents/skills/session-continuity/continuity.test.mjs +0 -381
- package/bin/install-session-continuity.mjs +0 -131
- package/bin/install-session-continuity.test.mjs +0 -106
- package/bin/install.pstack-plugin.test.mjs +0 -115
- package/bin/pstack-codex-discovery.test.mjs +0 -79
- package/bin/pstack-model-preferences.claude.json +0 -23
- package/bin/pstack-model-preferences.codex.json +0 -24
- package/bin/pstack.mjs +0 -392
- package/bin/pstack.test.mjs +0 -347
- package/bin/refresh-pstack-plugin-skills.test.mjs +0 -211
- package/bin/select-pstack-models.test.mjs +0 -727
- package/hooks/blocking/test_code_rules_enforcer_file_global_constants.py +0 -231
- package/rules/agent-spawn-protocol.md +0 -9
- package/rules/ask-user-question-required.md +0 -31
- package/rules/claims-as-quotes.md +0 -55
- package/rules/confirm-implementation-forks.md +0 -11
- package/rules/file-global-constants.md +0 -81
- package/rules/hedging-claims.md +0 -7
- package/rules/measurement-denominators.md +0 -39
- package/rules/prompt-workflow-context-controls.md +0 -19
- package/rules/pstack-host-mapping.md +0 -48
- package/rules/pstack-models.md +0 -142
- package/rules/vault-context.md +0 -5
- package/scripts/pstack-adapters/common.md +0 -17
- package/scripts/pstack-adapters/create-skill.md +0 -14
- package/scripts/pstack-adapters/host-claude.md +0 -7
- package/scripts/pstack-adapters/host-codex.md +0 -11
- package/scripts/pstack-adapters/host-cursor.md +0 -7
- package/scripts/pstack.lock.json +0 -11
- package/scripts/refresh_pstack_plugin_skills.mjs +0 -200
- package/scripts/select_pstack_models.mjs +0 -469
- /package/.agents/{skills → skills-archived}/source-command-logifix/SKILL.md +0 -0
package/rules/pstack-models.md
DELETED
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
# Pstack delegation policy
|
|
2
|
-
|
|
3
|
-
Pstack uses portable role requirements. Model IDs belong to host preference
|
|
4
|
-
files. The active native subagent tool decides which model IDs the current host
|
|
5
|
-
accepts.
|
|
6
|
-
|
|
7
|
-
This policy replaces the fixed model defaults in pstack skills and setup-pstack's
|
|
8
|
-
legacy step that writes one universal model rule. Setup updates only the selected
|
|
9
|
-
host preference JSON. The installer keeps this portable policy current.
|
|
10
|
-
|
|
11
|
-
## Setup
|
|
12
|
-
|
|
13
|
-
When setup-pstack runs, read the active native subagent tool metadata. Use the
|
|
14
|
-
host name from that tool or the current host context. Do not infer the host from
|
|
15
|
-
CODEX_HOME, CLAUDE_CONFIG_DIR, or another environment variable.
|
|
16
|
-
|
|
17
|
-
Save choices in the agents home under
|
|
18
|
-
`rules/pstack-model-preferences.<host>.json`. Keep each host in its own file.
|
|
19
|
-
The file contains this shape:
|
|
20
|
-
|
|
21
|
-
```json
|
|
22
|
-
{
|
|
23
|
-
"host": "current-host",
|
|
24
|
-
"defaultEffort": "medium",
|
|
25
|
-
"modelsByRole": {
|
|
26
|
-
"feature, refactoring": ["confirmed-host-model"]
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
Automatic model and effort routing reads
|
|
32
|
-
`rules/subagent-model-policy.json` from the same agents home. Edit that file to
|
|
33
|
-
change the next routing decision. The installer creates it only when it is
|
|
34
|
-
absent. The native hook blocks advisor role claims because the current Codex hook
|
|
35
|
-
input has no host-provided role binding. The Python advisor helper passes trusted
|
|
36
|
-
session metadata to the shared resolver. The native advisor hook path stays
|
|
37
|
-
pending until the host supplies that binding.
|
|
38
|
-
|
|
39
|
-
Offer only model IDs the active native tool accepts. Keep the user's order.
|
|
40
|
-
Treat `inherit-parent` and `auto` as parent inheritance, not model IDs. A full
|
|
41
|
-
install seeds the Claude file and the Codex file from the package when a host
|
|
42
|
-
file is absent, and leaves an existing file untouched. A different host never
|
|
43
|
-
copies another host's preference file.
|
|
44
|
-
|
|
45
|
-
## Selection before every delegation
|
|
46
|
-
|
|
47
|
-
Refresh the accepted model IDs from the active native subagent tool immediately
|
|
48
|
-
before each native delegation. Run the installed selector once for each agent.
|
|
49
|
-
Use `~/.agents/scripts/select_pstack_models.mjs` for the main profile. For a
|
|
50
|
-
named or explicit profile, use the selector under that profile's sibling agents
|
|
51
|
-
home.
|
|
52
|
-
|
|
53
|
-
Send one JSON object through standard input. Use the active host shell on
|
|
54
|
-
other operating systems:
|
|
55
|
-
|
|
56
|
-
```powershell
|
|
57
|
-
$selectorInput = @'
|
|
58
|
-
{
|
|
59
|
-
"host": "current-host",
|
|
60
|
-
"inventoryHost": "current-host",
|
|
61
|
-
"role": "how critics",
|
|
62
|
-
"delegationIndex": 0,
|
|
63
|
-
"panel": {
|
|
64
|
-
"agentCount": 3,
|
|
65
|
-
"requiresDistinctModels": false
|
|
66
|
-
},
|
|
67
|
-
"requestedEffort": "medium",
|
|
68
|
-
"availableModelIds": ["confirmed-host-model"],
|
|
69
|
-
"confirmedSuitableModelIds": [],
|
|
70
|
-
"parentModelId": "current-parent-model",
|
|
71
|
-
"parentFallback": {
|
|
72
|
-
"isAllowed": true,
|
|
73
|
-
"hasMaterialCapabilityLoss": false
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
'@
|
|
77
|
-
$selection = $selectorInput | node "$HOME/.agents/scripts/select_pstack_models.mjs"
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
`availableModelIds` comes from the current native tool metadata.
|
|
81
|
-
`confirmedSuitableModelIds` contains available alternatives whose suitability
|
|
82
|
-
for this role the host metadata or user has confirmed. Do not rank or classify a
|
|
83
|
-
model from its name.
|
|
84
|
-
Keep the host's default effort in the preference file as `defaultEffort`, or
|
|
85
|
-
pass it as trusted runtime data in `requestedEffort`. The selector applies that
|
|
86
|
-
effort to a model-only entry before it picks a candidate. It holds the entry
|
|
87
|
-
when neither source supplies an effort and the policy has no single approved
|
|
88
|
-
effort for that model and role.
|
|
89
|
-
|
|
90
|
-
Use the returned `nativeSpawnArguments` in the native subagent call. An empty
|
|
91
|
-
object with `omitNativeModelArgument: true` means parent inheritance, so omit
|
|
92
|
-
the native `model` argument. Stop when `canDelegate` is false. Ask the user
|
|
93
|
-
when `requiresUserChoice` is true.
|
|
94
|
-
|
|
95
|
-
When a preference entry includes `{ "model": "model-id", "effort": "medium" }`,
|
|
96
|
-
the selector routes the pair before it deduplicates candidates and checks panel
|
|
97
|
-
diversity. The selected pair appears in `panel.selectedModelPairs`, and the
|
|
98
|
-
native argument uses `reasoning_effort`. The policy's `selectorExclusions` list
|
|
99
|
-
removes matching routed pairs from panel contenders without blocking direct
|
|
100
|
-
resolver or spawn-hook use.
|
|
101
|
-
|
|
102
|
-
Run the selector again before the next agent call. Pass the same panel contract
|
|
103
|
-
and the new delegation index with the refreshed inventory. Report
|
|
104
|
-
`panel.selectedModelIds` and `panel.isCrossModelDiverse` as returned. A panel
|
|
105
|
-
that repeats one model is a same-model panel.
|
|
106
|
-
|
|
107
|
-
## Portable role requirements
|
|
108
|
-
|
|
109
|
-
feature, refactoring: reliable code execution
|
|
110
|
-
bug-fix: careful diagnosis and code execution
|
|
111
|
-
perf-issue: systems reasoning and performance analysis
|
|
112
|
-
hillclimb: strong iterative judgment
|
|
113
|
-
judgment and prose: strong judgment and clear prose
|
|
114
|
-
hardest tasks: highest available reasoning capability
|
|
115
|
-
how explorer: fast codebase exploration
|
|
116
|
-
how explainer: clear technical synthesis
|
|
117
|
-
how critics: independent technical criticism
|
|
118
|
-
why investigators: fast evidence gathering
|
|
119
|
-
why synthesizer: strong evidence synthesis
|
|
120
|
-
reflect tooling: reliable tool and workflow analysis
|
|
121
|
-
reflect judgment, divergent, synthesizer: strong judgment and divergent analysis
|
|
122
|
-
arena runners: independent solution development
|
|
123
|
-
arena cross-judge pool: independent comparative judgment
|
|
124
|
-
swarm workers: fast bounded task execution
|
|
125
|
-
architect runners: independent architecture design
|
|
126
|
-
interrogate reviewers: independent adversarial review
|
|
127
|
-
|
|
128
|
-
## Panel requirements
|
|
129
|
-
|
|
130
|
-
Panel size and model diversity are separate from model preferences.
|
|
131
|
-
|
|
132
|
-
- How critics use three agents by default. Cross-model diversity is optional.
|
|
133
|
-
- Arena runners use three agents by default and require distinct models.
|
|
134
|
-
- The arena cross-judge pool selects one judge. Pass `parentModelId` so the
|
|
135
|
-
selector chooses another available preference before the parent's model.
|
|
136
|
-
- Swarm uses the requested worker count. Same-model workers are valid.
|
|
137
|
-
- Architect uses two agents by default and requires distinct models.
|
|
138
|
-
- Interrogate uses three agents by default and requires distinct models.
|
|
139
|
-
|
|
140
|
-
A required-diversity panel stops when the current host lacks enough distinct
|
|
141
|
-
confirmed models. Parent inheritance is the last fallback. When inheritance
|
|
142
|
-
causes material capability loss, stop and ask the user before delegating.
|
package/rules/vault-context.md
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
# Obsidian vault context
|
|
2
|
-
|
|
3
|
-
An Obsidian vault holds session reports (`sessions/`), decisions (`decisions/`), and research (`Research/`) across projects.
|
|
4
|
-
|
|
5
|
-
Before project work, search the vault with the obsidian MCP tools. Search by `project` frontmatter first, then by keyword. Search again when a component has known history, or when a task might repeat or reverse a prior decision.
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
# Pstack compatibility
|
|
2
|
-
|
|
3
|
-
Apply this mapping to the upstream workflow below. User instructions and host permissions stay in charge.
|
|
4
|
-
|
|
5
|
-
Determine the active host from the session's native tools. Read host-claude.md, host-codex.md, or host-cursor.md beside this file for the active host. Read pstack-host-mapping.md and pstack-models.md beside this file before delegation. Those files are copied from claude-dev-env's existing rules into this immutable release. Run the selectorPath recorded in release.json and supply its preferencesDirectory in the selector input. This overrides the old global selector path. Model preferences remain outside the release.
|
|
6
|
-
|
|
7
|
-
Read the installed inventory in release.json for exact skill identities. Pstack skills use the `pstack:<slug>` plugin namespace. A pstack request for how, /how, or pstack:how means pstack:how. Cursor-team-kit skills keep flat component-prefixed names, so a request for deslop means cursor-team-kit-deslop. Resolve all other skill names through the inventory. Read each leaf skill in full when it applies. Keep each skill's scripts, references, playbooks, and agent definitions at its recorded source root.
|
|
8
|
-
|
|
9
|
-
Every delegation prompt must include this compatibility file's absolute path, the active host file's absolute path, the selected skill's absolute path, and the requested upstream agent definition's absolute path when present. Tell the child to read them before working and to put the complete findings and evidence in its final message. The child receives the same host and model mapping as the parent.
|
|
10
|
-
|
|
11
|
-
Treat upstream model identifiers as role preferences. Use only models and arguments accepted by the native tool in this session. Preserve required independence and model diversity. Report a missing capability when the session cannot supply the required independent panel. Use the native tool's supported concurrency and wait operations.
|
|
12
|
-
|
|
13
|
-
For setup-pstack, configure only the current host's model preference file described in pstack-models.md. Keep that file outside this release. The Cursor .mdc output is a Cursor rule, not a cross-host model configuration.
|
|
14
|
-
|
|
15
|
-
For create-skill, use Cursor's built-in skill on Cursor and cde-create-skill on Claude or Codex. For control-cli and control-ui, load the corresponding installed cursor-team-kit skill and use native terminal or browser tools. Check that the required CLI, browser, credentials, and permissions exist before executing its steps. State an unavailable dependency explicitly. Never claim an unattended browser or remote session ran from a filesystem check.
|
|
16
|
-
|
|
17
|
-
Updates are prepared outside the active release. Follow absolute paths into the release named in this entry point for the rest of this task. Preserve supporting files and scripts, including executable modes and licenses.
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: cde-create-skill
|
|
3
|
-
description: Write or update a portable SKILL.md workflow when pstack requests create-skill on a host without Cursor's built-in skill.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Create a portable skill
|
|
7
|
-
|
|
8
|
-
State the user-visible outcome and the exact trigger first. Inspect applicable repository instructions and existing skills before adding a new skill.
|
|
9
|
-
|
|
10
|
-
Create a lowercase, hyphenated skill directory under the project's .claude/skills. Give SKILL.md YAML frontmatter with a matching name and a one-line description that says when to use it. Put the workflow after the frontmatter. Keep supporting scripts and references together with the skill, and use relative links inside it. Add a Codex .agents/skills directory link where needed, preserving existing files.
|
|
11
|
-
|
|
12
|
-
Use native host tools and capability checks. Give each step a concrete input, action, and observable result. Separate human choices from facts the agent can inspect. Read referenced instructions before using them.
|
|
13
|
-
|
|
14
|
-
Verify metadata, links, and script behavior. Then invoke the installed skill in the intended host and record the actual result. Distinguish a filesystem check from a native invocation. Keep the scope to the requested workflow.
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
# Claude Code
|
|
2
|
-
|
|
3
|
-
Use Claude Code's native Skill mechanism to load installed skills. Use Agent for Task requests, with only arguments the session exposes. For an upstream poteto-agent or another unavailable agent type, use an available general-purpose agent and include the upstream definition and compatibility files in its prompt. A file on disk does not register a native agent type.
|
|
4
|
-
|
|
5
|
-
Use AskUserQuestion for a required human choice. Use Read, Glob, Grep, Edit, Write, Bash, and the available browser tools for their corresponding work. Read native tool schemas rather than forwarding Cursor-only arguments. Use background execution only when the current tool supports it.
|
|
6
|
-
|
|
7
|
-
Choose model aliases accepted by this session. Omit the model argument for permitted parent inheritance. Keep host preferences in the profile's agents-home rules/pstack-model-preferences.claude.json. Use the current session's transcript location, or state that transcripts are unavailable.
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
# Codex
|
|
2
|
-
|
|
3
|
-
Load SKILL.md with the native skill mechanism or read its complete file with the available file tool. Codex does not need a Claude Skill tool.
|
|
4
|
-
|
|
5
|
-
Map Task or Agent to the native spawn_agent, wait, send_input, and close_agent operations that this session exposes. Field names vary by host version. Inspect the live schema. For poteto-agent, include the upstream agent definition, skill entry, and compatibility files in the child prompt. Select an available native agent type. Do not pass subagent_type, run_in_background, or a model argument unless that tool accepts it.
|
|
6
|
-
|
|
7
|
-
Use the native shell for commands, apply_patch or the native editor for edits, and the available browser tool for UI work. Ask the user through the current session's supported interaction mechanism for a genuine preference decision.
|
|
8
|
-
|
|
9
|
-
A multi-agent feature setting in a CLI profile does not establish subagent support in a cloud session. Check available tools. Required independent or cross-model review stops with a clear capability report when that session cannot provide it. Keep configured model preferences in the profile's agents-home rules/pstack-model-preferences.codex.json. Read the current transcript location from host context, not ~/.cursor/projects.
|
|
10
|
-
|
|
11
|
-
The selector's availableModelIds contains only models selectable through this session's delegation tool. A tool without a model argument supplies an empty list and uses permitted parent inheritance. A provider's general model catalog is not proof of per-delegation model selection.
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
# Cursor
|
|
2
|
-
|
|
3
|
-
Use Cursor's available native skill and delegation tools. The installed component-prefixed names in release.json replace upstream plugin short names. Keep project skills under .claude/skills and use the .agents/skills links for shared discovery. The installer writes no skills under .cursor.
|
|
4
|
-
|
|
5
|
-
For an upstream custom agent type absent from the live tool inventory, use an available native agent type and include that upstream agent definition, the skill entry, and compatibility files in the child prompt. Use the session's supported model identifiers and delegation fields. Preserve required panel independence and model diversity.
|
|
6
|
-
|
|
7
|
-
Use Cursor's built-in create-skill when present. Otherwise read cde-create-skill. Keep Cursor-specific preferences separate from Claude and Codex preferences. Resolve transcripts from the current workspace and session context.
|
package/scripts/pstack.lock.json
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"schemaVersion": 1,
|
|
3
|
-
"adapterVersion": 1,
|
|
4
|
-
"repository": "https://github.com/cursor/plugins.git",
|
|
5
|
-
"commit": "27e2a62ff94f9af4b5e68435e41cdceacadb840c",
|
|
6
|
-
"components": ["pstack", "cursor-team-kit"],
|
|
7
|
-
"requiredSkills": {
|
|
8
|
-
"pstack": ["poteto-mode"],
|
|
9
|
-
"cursor-team-kit": ["deslop", "control-cli", "control-ui"]
|
|
10
|
-
}
|
|
11
|
-
}
|
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Keep a pstack skills-directory plugin manifest listing every skill folder.
|
|
3
|
-
*
|
|
4
|
-
* Claude Code loads a folder under `~/.claude/skills` as a plugin when the
|
|
5
|
-
* folder holds `.claude-plugin/plugin.json`, and it namespaces that plugin's
|
|
6
|
-
* skills as `pstack:how`. It does not scan a plugin root for skill folders, so
|
|
7
|
-
* the manifest names each sub-skill path:
|
|
8
|
-
*
|
|
9
|
-
* plugin root holds: how/SKILL.md, why/SKILL.md, docs/, .claude-plugin/
|
|
10
|
-
* manifest before: "skills": ["./how"]
|
|
11
|
-
* manifest after: "skills": ["./how", "./why"] <- why/ picked up
|
|
12
|
-
* second run: no write <- the list matches
|
|
13
|
-
* docs/, .claude-plugin/ <- skipped, no SKILL.md
|
|
14
|
-
*
|
|
15
|
-
* The installer calls `refreshPstackPluginManifest` for a pstack folder the
|
|
16
|
-
* user installed. Running this file directly refreshes the same manifest after
|
|
17
|
-
* a pstack update.
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
import {
|
|
21
|
-
existsSync,
|
|
22
|
-
mkdirSync,
|
|
23
|
-
readdirSync,
|
|
24
|
-
readFileSync,
|
|
25
|
-
realpathSync,
|
|
26
|
-
writeFileSync,
|
|
27
|
-
} from 'node:fs';
|
|
28
|
-
import { homedir } from 'node:os';
|
|
29
|
-
import { join } from 'node:path';
|
|
30
|
-
import { fileURLToPath } from 'node:url';
|
|
31
|
-
|
|
32
|
-
export const PSTACK_PLUGIN_DIRECTORY_NAME = 'pstack';
|
|
33
|
-
export const PSTACK_PLUGIN_MANIFEST_RELATIVE_PATH = join('.claude-plugin', 'plugin.json');
|
|
34
|
-
export const SKILL_MANIFEST_FILE_NAME = 'SKILL.md';
|
|
35
|
-
export const SKILL_PATH_PREFIX = './';
|
|
36
|
-
|
|
37
|
-
const CLAUDE_HOME_DIRECTORY_NAME = '.claude';
|
|
38
|
-
const MANIFEST_SKILLS_KEY = 'skills';
|
|
39
|
-
const MANIFEST_INDENT_SPACES = 2;
|
|
40
|
-
const FILE_ENCODING = 'utf8';
|
|
41
|
-
const HIDDEN_NAME_PREFIX = '.';
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Fields a first write seeds from the published pstack plugin. An existing
|
|
45
|
-
* manifest keeps its own values. This refresher changes only the skills list.
|
|
46
|
-
*/
|
|
47
|
-
const SEEDED_MANIFEST_FIELDS = Object.freeze({
|
|
48
|
-
$schema: 'https://anthropic.com/claude-code/plugin.schema.json',
|
|
49
|
-
name: PSTACK_PLUGIN_DIRECTORY_NAME,
|
|
50
|
-
version: '0.14.8',
|
|
51
|
-
description:
|
|
52
|
-
'if you want to go fast, go deep first. pstack helps you write less, but higher '
|
|
53
|
-
+ 'quality code. rigorous agent workflows you can parallelize with confidence.',
|
|
54
|
-
author: Object.freeze({ name: 'Lauren Tan' }),
|
|
55
|
-
homepage: 'https://github.com/cursor/plugins/tree/main/pstack',
|
|
56
|
-
repository: 'https://github.com/cursor/plugins',
|
|
57
|
-
license: 'MIT',
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Name every sub-folder of a plugin root that Claude Code loads as a skill.
|
|
62
|
-
*
|
|
63
|
-
* @param {string} pluginRoot Plugin folder holding one sub-folder per skill.
|
|
64
|
-
* @returns {string[]} The sorted names of each visible sub-folder holding a
|
|
65
|
-
* skill manifest. A hidden folder and a folder without `SKILL.md` are left out.
|
|
66
|
-
*/
|
|
67
|
-
export function findPstackSkillDirectoryNames(pluginRoot) {
|
|
68
|
-
return readdirSync(pluginRoot, { withFileTypes: true })
|
|
69
|
-
.filter((eachEntry) => eachEntry.isDirectory())
|
|
70
|
-
.map((eachEntry) => eachEntry.name)
|
|
71
|
-
.filter((eachName) => !eachName.startsWith(HIDDEN_NAME_PREFIX))
|
|
72
|
-
.filter((eachName) => existsSync(join(pluginRoot, eachName, SKILL_MANIFEST_FILE_NAME)))
|
|
73
|
-
.sort();
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function readManifestSkillPaths(manifestPath) {
|
|
77
|
-
if (!existsSync(manifestPath)) return { manifest: null, previousSkillPaths: [] };
|
|
78
|
-
const manifest = JSON.parse(readFileSync(manifestPath, FILE_ENCODING));
|
|
79
|
-
const listedSkillPaths = manifest[MANIFEST_SKILLS_KEY];
|
|
80
|
-
if (typeof listedSkillPaths === 'string') {
|
|
81
|
-
return { manifest, previousSkillPaths: [listedSkillPaths] };
|
|
82
|
-
}
|
|
83
|
-
if (Array.isArray(listedSkillPaths)) {
|
|
84
|
-
return { manifest, previousSkillPaths: listedSkillPaths };
|
|
85
|
-
}
|
|
86
|
-
return { manifest, previousSkillPaths: [] };
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Rewrite a pstack plugin manifest's skills list to match its folder.
|
|
91
|
-
*
|
|
92
|
-
* Writes only when the list changes. A second call on an unchanged folder
|
|
93
|
-
* leaves the file byte for byte. An existing manifest keeps every field but
|
|
94
|
-
* the skills list.
|
|
95
|
-
*
|
|
96
|
-
* @param {string} pluginRoot Plugin folder holding the skill sub-folders.
|
|
97
|
-
* @returns {{didWrite: boolean, reason: string, previousSkillPaths: string[],
|
|
98
|
-
* currentSkillPaths: string[]}} Whether this call wrote, and the paths
|
|
99
|
-
* before and after. `reason` names a skip, and is empty on a write or an
|
|
100
|
-
* unchanged list.
|
|
101
|
-
*/
|
|
102
|
-
export function refreshPstackPluginManifest(pluginRoot) {
|
|
103
|
-
const declined = { didWrite: false, previousSkillPaths: [], currentSkillPaths: [] };
|
|
104
|
-
if (!existsSync(pluginRoot)) {
|
|
105
|
-
return { ...declined, reason: 'plugin root is absent' };
|
|
106
|
-
}
|
|
107
|
-
if (existsSync(join(pluginRoot, SKILL_MANIFEST_FILE_NAME))) {
|
|
108
|
-
return { ...declined, reason: 'plugin root is itself a skill' };
|
|
109
|
-
}
|
|
110
|
-
const allSkillNames = findPstackSkillDirectoryNames(pluginRoot);
|
|
111
|
-
if (allSkillNames.length === 0) {
|
|
112
|
-
return { ...declined, reason: 'plugin root holds no skills' };
|
|
113
|
-
}
|
|
114
|
-
const manifestPath = join(pluginRoot, PSTACK_PLUGIN_MANIFEST_RELATIVE_PATH);
|
|
115
|
-
const { manifest, previousSkillPaths } = readManifestSkillPaths(manifestPath);
|
|
116
|
-
const currentSkillPaths = allSkillNames.map((eachName) => SKILL_PATH_PREFIX + eachName);
|
|
117
|
-
const outcome = { reason: '', previousSkillPaths, currentSkillPaths };
|
|
118
|
-
const isUnchanged = manifest !== null
|
|
119
|
-
&& previousSkillPaths.length === currentSkillPaths.length
|
|
120
|
-
&& previousSkillPaths.every((eachPath, eachIndex) => eachPath === currentSkillPaths[eachIndex]);
|
|
121
|
-
if (isUnchanged) {
|
|
122
|
-
return { ...outcome, didWrite: false };
|
|
123
|
-
}
|
|
124
|
-
const refreshedManifest = {
|
|
125
|
-
...(manifest === null ? SEEDED_MANIFEST_FIELDS : manifest),
|
|
126
|
-
[MANIFEST_SKILLS_KEY]: currentSkillPaths,
|
|
127
|
-
};
|
|
128
|
-
mkdirSync(join(pluginRoot, '.claude-plugin'), { recursive: true });
|
|
129
|
-
writeFileSync(
|
|
130
|
-
manifestPath,
|
|
131
|
-
JSON.stringify(refreshedManifest, null, MANIFEST_INDENT_SPACES) + '\n',
|
|
132
|
-
FILE_ENCODING,
|
|
133
|
-
);
|
|
134
|
-
return { ...outcome, didWrite: true };
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Return the pstack plugin folder under a Claude home.
|
|
139
|
-
*
|
|
140
|
-
* @param {string} claudeHome The Claude home directory holding `skills`.
|
|
141
|
-
* @returns {string} The pstack plugin folder path, present or not.
|
|
142
|
-
*/
|
|
143
|
-
export function pstackPluginRootFor(claudeHome) {
|
|
144
|
-
return join(claudeHome, 'skills', PSTACK_PLUGIN_DIRECTORY_NAME);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* Return the Claude home used when no folder argument is given.
|
|
149
|
-
*
|
|
150
|
-
* A named profile sets `CLAUDE_CONFIG_DIR`. The installer writes into that
|
|
151
|
-
* root. The command-line form reads the same variable.
|
|
152
|
-
*
|
|
153
|
-
* @param {Record<string, string|undefined>} environment The process environment.
|
|
154
|
-
* @returns {string} The Claude home directory holding `skills`.
|
|
155
|
-
*/
|
|
156
|
-
export function claudeHomeFrom(environment) {
|
|
157
|
-
return environment.CLAUDE_CONFIG_DIR || join(homedir(), CLAUDE_HOME_DIRECTORY_NAME);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
function main() {
|
|
161
|
-
const explicitRoot = process.argv[2];
|
|
162
|
-
const pluginRoot = explicitRoot || pstackPluginRootFor(claudeHomeFrom(process.env));
|
|
163
|
-
const outcome = refreshPstackPluginManifest(pluginRoot);
|
|
164
|
-
if (!outcome.didWrite && outcome.reason) {
|
|
165
|
-
console.log(`No manifest written: ${outcome.reason}.`);
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
|
-
if (!outcome.didWrite) {
|
|
169
|
-
console.log(`No change. ${outcome.currentSkillPaths.length} skills listed.`);
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
console.log(`Updated. ${outcome.currentSkillPaths.length} skills listed.`);
|
|
173
|
-
for (const eachPath of outcome.currentSkillPaths) {
|
|
174
|
-
if (!outcome.previousSkillPaths.includes(eachPath)) console.log(` added ${eachPath}`);
|
|
175
|
-
}
|
|
176
|
-
for (const eachPath of outcome.previousSkillPaths) {
|
|
177
|
-
if (!outcome.currentSkillPaths.includes(eachPath)) console.log(` removed ${eachPath}`);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
* Return whether node ran this module as its entry point.
|
|
183
|
-
*
|
|
184
|
-
* A directory link to this script gives node an invoked path that differs from
|
|
185
|
-
* this module's own path. Both sides resolve to their real location before
|
|
186
|
-
* the comparison.
|
|
187
|
-
*
|
|
188
|
-
* @param {string|undefined} invokedPath The path node reports as `argv[1]`.
|
|
189
|
-
* @returns {boolean} True when node ran this module as its entry point.
|
|
190
|
-
*/
|
|
191
|
-
function isEntryPoint(invokedPath) {
|
|
192
|
-
if (!invokedPath) return false;
|
|
193
|
-
try {
|
|
194
|
-
return realpathSync(fileURLToPath(import.meta.url)) === realpathSync(invokedPath);
|
|
195
|
-
} catch {
|
|
196
|
-
return fileURLToPath(import.meta.url) === invokedPath;
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
if (isEntryPoint(process.argv[1])) main();
|