claude-dev-env 8.85.0 → 8.85.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/.agents/skills/efficient-review/SKILL.md +79 -0
- package/.agents/skills/efficient-review/defaults.json +12 -0
- package/.agents/skills/efficient-review/reference/cloud-session.md +13 -0
- package/bin/codex-skill-load-block.mjs +27 -28
- package/bin/codex-skill-load-block.test.mjs +62 -30
- package/bin/codex-trimmed-sol.test.mjs +1 -0
- package/bin/ever-shipped-skills.mjs +1 -0
- package/bin/install-deepstack-plugin.mjs +300 -0
- package/bin/install-deepstack-plugin.test.mjs +98 -0
- package/bin/install-pstack-plugin.mjs +3 -5
- package/bin/install-pstack-plugin.test.mjs +28 -0
- package/bin/install.agents-home.test.mjs +1 -0
- package/bin/install.codex-rules.test.mjs +2 -0
- package/bin/install.cursor-rules.test.mjs +1 -0
- package/bin/install.mjs +27 -3
- package/bin/install.profile-settings.test.mjs +1 -0
- package/bin/install.prune.test.mjs +20 -0
- package/bin/install.question-presentation.test.mjs +7 -3
- package/bin/install.settings-defaults.test.mjs +1 -0
- package/bin/install.shared-settings.test.mjs +1 -0
- package/bin/install.test.mjs +79 -10
- package/bin/install.transaction.test.mjs +8 -0
- package/bin/install.uninstall-transaction.test.mjs +2 -0
- package/bin/install.update-keeps-hooks.test.mjs +1 -0
- package/docs/decisions/deepstack-install.md +9 -0
- package/docs/rule-guides/deepstack-wording.md +23 -0
- package/package.json +1 -1
- package/rules/deepstack-wording.md +15 -0
- package/scripts/deepstack_inventory.py +348 -0
- package/scripts/dev_env_scripts_constants/deepstack_inventory_constants.py +42 -0
- package/scripts/test_deepstack_inventory.py +137 -0
- package/scripts/test_deepstack_wording_rule.py +20 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: efficient-review
|
|
3
|
+
description: >-
|
|
4
|
+
Run one low-cost code review of the current branch, or of a branch in another
|
|
5
|
+
repository, as a headless Claude run on haiku with the slim review profile.
|
|
6
|
+
An auto run lets the account broker pick the account. A headless run names
|
|
7
|
+
the account. Triggers: /efficient-review, "efficient review", "cheap review",
|
|
8
|
+
"haiku review of this branch".
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Efficient review
|
|
12
|
+
|
|
13
|
+
This skill runs one headless code review of a branch on haiku and prints its findings. Pick the mode, run the planner, then run the review on the machine that holds the Claude accounts, or through the agent-pc runner from a cloud session.
|
|
14
|
+
|
|
15
|
+
Argument shape: `/efficient-review [auto|headless] [account] [model=] [effort=] [level=] [base=]`. Pass `model=` only as the Defaults section allows.
|
|
16
|
+
A blank argument comes from [`defaults.json`](defaults.json).
|
|
17
|
+
|
|
18
|
+
## Defaults
|
|
19
|
+
|
|
20
|
+
**Always run the review on haiku.** `defaults.json` names `haiku`, and a blank `model` input runs it. Pass `model=` or the workflow's `model` input only when the owner's own message names the family as a directive, such as "run this review on sonnet". Then run it on that family. Do not ask which model to use. Do not change the model on your own, for depth, for cost, or because passes keep finding issues. A stream of findings is a reason to fix the findings and run haiku again.
|
|
21
|
+
|
|
22
|
+
**The review must run headless.** Never spawn the review as an Agent subagent in this session. A subagent runs inside the parent session, so it cannot load the slim profile's setting sources, plugin folder, MCP config, or auto-compact window. `defaults.json` holds a headless mode, and `load_defaults` refuses any other mode.
|
|
23
|
+
|
|
24
|
+
## Subcommands
|
|
25
|
+
|
|
26
|
+
Run each one as `python -m tools.efficient_review <subcommand> <arguments>` from the checkout that contains `tools/efficient_review.py`.
|
|
27
|
+
|
|
28
|
+
| Subcommand | Action |
|
|
29
|
+
|---|---|
|
|
30
|
+
| `plan` | Prints the mode, account, model family, harness, effort, level, base, and the prompt. |
|
|
31
|
+
| `accounts` | Lists the Claude accounts a headless run can name. |
|
|
32
|
+
| `run` | Runs a headless or auto review and prints the review, the served models, and the saved file. |
|
|
33
|
+
|
|
34
|
+
## Modes
|
|
35
|
+
|
|
36
|
+
| Mode | Picked when | Account |
|
|
37
|
+
|---|---|---|
|
|
38
|
+
| auto | No mode word and no account. The word `auto` picks it too. | The account broker picks it. Ask nothing. |
|
|
39
|
+
| headless | The word `headless`, or an account launcher name. | Required. With no account named, ask for one. |
|
|
40
|
+
|
|
41
|
+
A model is a family name: haiku, sonnet, opus, sol, or luna. The planner refuses a versioned model ID. `codex review` takes no level, so the planner refuses `level=` with sol or luna.
|
|
42
|
+
|
|
43
|
+
## 1. Headless mode with no account
|
|
44
|
+
|
|
45
|
+
Run `python -m tools.efficient_review accounts`. Ask with the session's question tool, one option per listed account, and wait for the answer. The answer is the account. On the machine that holds the launchers, the list comes from the `claude-*.cmd` launchers, without `claude-base`, `claude-raw`, and `claude-kimi`. In the cloud the list is the known roster.
|
|
46
|
+
|
|
47
|
+
## 2. Headless or auto on that machine
|
|
48
|
+
|
|
49
|
+
Run `python -m tools.efficient_review run <arguments> --repository <checkout>`. When the checkout has no launcher folder, go to step 3.
|
|
50
|
+
|
|
51
|
+
- It builds the slim profile from `tools.review_profile` into a temp folder.
|
|
52
|
+
- Auto asks `account_broker.py choose --product claude` for the account.
|
|
53
|
+
- It runs the account launcher with `-p`, the model alias, the effort, the profile arguments, and the environment. The system prompt file holds the review rules from the checkout that contains the runner, and the code standards. The prompt and the diff against the base go on standard input.
|
|
54
|
+
- A sol or luna family runs `codex review` on the newest slug of that family. The log prints that slug.
|
|
55
|
+
- It prints the review, the served models from the stream's `modelUsage` keys, and the saved file. The review and its record go to a temp folder outside the repository.
|
|
56
|
+
|
|
57
|
+
## 3. Headless or auto from a cloud session
|
|
58
|
+
|
|
59
|
+
The cloud container has no other Claude accounts, so the review runs on the agent-pc runner. Read [reference/cloud-session.md](reference/cloud-session.md).
|
|
60
|
+
|
|
61
|
+
## Reading guide
|
|
62
|
+
|
|
63
|
+
| File | What it does |
|
|
64
|
+
|---|---|
|
|
65
|
+
| [`reference/cloud-session.md`](reference/cloud-session.md) | Dispatching the review from a cloud session and reading its results branch. |
|
|
66
|
+
| [`defaults.json`](defaults.json) | The default model family, effort, level, prompt, base, profile, advisor, validator, mode, and timeout. |
|
|
67
|
+
|
|
68
|
+
## Common pitfalls
|
|
69
|
+
|
|
70
|
+
- **Picking a stronger model after several passes.** Fix the findings and run haiku again.
|
|
71
|
+
- **Setting a valid finding aside.** Check each finding against the code. Fix every one that holds in the same branch, even when it reaches files the diff did not touch. Answer only the findings that fail the check, with the file and line that rules each out.
|
|
72
|
+
- **Asking which model to use.** The answer is haiku. When the owner names another family, run it without a confirmation question.
|
|
73
|
+
- **Spawning the review as a subagent.** The review loses the slim profile. Run it headless.
|
|
74
|
+
- **Dispatching from the branch under review.** The workflow runs only from `main`.
|
|
75
|
+
|
|
76
|
+
## Out of scope
|
|
77
|
+
|
|
78
|
+
- The `validator` default stays `off`.
|
|
79
|
+
- A fixer step after the review. The skill has no fixer step.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Headless or auto from a cloud session
|
|
2
|
+
|
|
3
|
+
The cloud container has no other Claude accounts, so the review runs on the agent-pc runner.
|
|
4
|
+
|
|
5
|
+
1. From the checkout that contains `tools/efficient_review.py`, dispatch `.github/workflows/efficient-review.yml` on ref `main`. The job runs only from `main`, so the runner executes the reviewed tool code and never the branch under review. Inputs: `account` (blank means auto), `effort`, `level`, `base`, `ref` (the branch to review), and `repository` (the `owner/name` to review when it is not that checkout). Leave `model` blank.
|
|
6
|
+
2. Wait for the run to finish.
|
|
7
|
+
3. Read `review.md` from the results branch `efficient-review-results/<run id>-<attempt>`. The branch holds that file and `review.json`. The commit message names the account, the launcher, and the served models. Print the review and the served models.
|
|
8
|
+
|
|
9
|
+
A review of another repository checks it out with the read token configured for that workflow.
|
|
10
|
+
|
|
11
|
+
To review a pull request in the checkout that contains the workflow, pass its number as `pull_request` and leave `ref` blank. The run reviews the pull request head, then posts each finding on it as a review thread.
|
|
12
|
+
|
|
13
|
+
For a pull request in this package's public repository, pass `repository` as `jl-cmd/claude-dev-env` and `pull_request` together. The workflow accepts that repository name. The run posts nothing on the public pull request. When the review lists findings, it dispatches the landing repair with the pull request number and the results branch.
|
|
@@ -3,9 +3,6 @@ import { dirname, join, resolve } from 'node:path';
|
|
|
3
3
|
|
|
4
4
|
export const SKILL_LOAD_BLOCK_START = '<!-- claude-dev-env skill load: start -->';
|
|
5
5
|
export const SKILL_LOAD_BLOCK_END = '<!-- claude-dev-env skill load: end -->';
|
|
6
|
-
export const SKILL_LOAD_INSTRUCTION = 'Load the pstack:poteto-mode skill before your first tool call or reply: '
|
|
7
|
-
+ 'open its SKILL.md from your skills list and follow it. This binds every Codex agent, '
|
|
8
|
-
+ 'including a helper started with spawn_agent, whatever its task message says.';
|
|
9
6
|
export const PACKAGE_GUIDANCE_BLOCK_START = '<!-- claude-dev-env package guidance: start -->';
|
|
10
7
|
export const PACKAGE_GUIDANCE_BLOCK_END = '<!-- claude-dev-env package guidance: end -->';
|
|
11
8
|
export const QUESTION_PRESENTATION_BLOCK_START = '<!-- claude-dev-env question presentation: start -->';
|
|
@@ -39,17 +36,24 @@ function withBlockReplaced(guidanceText, blockStart, blockEnd, block) {
|
|
|
39
36
|
}
|
|
40
37
|
|
|
41
38
|
/**
|
|
42
|
-
* Return
|
|
39
|
+
* Return guidance text with every complete skill-load block removed.
|
|
43
40
|
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
* the text is replaced where it stands; otherwise the block goes first.
|
|
41
|
+
* When a block is the start of the remaining text, one following blank line is
|
|
42
|
+
* removed with it. A start marker with no end marker stays.
|
|
47
43
|
*/
|
|
48
|
-
export function
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
44
|
+
export function withoutSkillLoadBlock(guidanceText) {
|
|
45
|
+
let remainingText = guidanceText;
|
|
46
|
+
let removedBlockCount = 0;
|
|
47
|
+
while (remainingText.includes(SKILL_LOAD_BLOCK_START)) {
|
|
48
|
+
const blockStartsAtTop = remainingText.startsWith(SKILL_LOAD_BLOCK_START);
|
|
49
|
+
const replacedText = withBlockReplaced(remainingText, SKILL_LOAD_BLOCK_START, SKILL_LOAD_BLOCK_END, '');
|
|
50
|
+
if (replacedText === null || replacedText === remainingText) break;
|
|
51
|
+
removedBlockCount += 1;
|
|
52
|
+
remainingText = blockStartsAtTop && replacedText.startsWith('\n')
|
|
53
|
+
? replacedText.slice(1)
|
|
54
|
+
: replacedText;
|
|
55
|
+
}
|
|
56
|
+
return removedBlockCount === 0 ? guidanceText : remainingText;
|
|
53
57
|
}
|
|
54
58
|
|
|
55
59
|
function withPackageGuidanceBlock(guidanceText, packageGuidanceText) {
|
|
@@ -84,7 +88,7 @@ function writeCodexGuidanceFile(codexHome, allPackageGuidancePaths, transformGui
|
|
|
84
88
|
const isPackageGuidanceLink = agentsEntry?.isSymbolicLink() ?? false;
|
|
85
89
|
if (isPackageGuidanceLink && !linksToPackageGuidance(agentsPath, allPackageGuidancePaths)) return null;
|
|
86
90
|
const currentText = agentsEntry && !isPackageGuidanceLink ? readFileSync(agentsPath, 'utf8') : '';
|
|
87
|
-
const updatedText = transformGuidance(currentText);
|
|
91
|
+
const updatedText = transformGuidance(withoutSkillLoadBlock(currentText));
|
|
88
92
|
if (!isPackageGuidanceLink && updatedText === currentText) return null;
|
|
89
93
|
mkdirSync(codexHome, { recursive: true });
|
|
90
94
|
if (isPackageGuidanceLink) unlinkSync(agentsPath);
|
|
@@ -93,29 +97,24 @@ function writeCodexGuidanceFile(codexHome, allPackageGuidancePaths, transformGui
|
|
|
93
97
|
}
|
|
94
98
|
|
|
95
99
|
/**
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
* The skill-load block goes first, and the package-guidance block holds the
|
|
99
|
-
* current guidance text. A symbolic link that points at the package guidance
|
|
100
|
-
* (the shared agents-home copy, or the retired copy under the Claude home) is
|
|
101
|
-
* replaced by a regular file holding both blocks, so Codex keeps both after
|
|
102
|
-
* the package moves its guidance. A link to any other file is left alone.
|
|
100
|
+
* Remove a skill-load block and write package guidance into CODEX_HOME/AGENTS.md.
|
|
103
101
|
*
|
|
104
|
-
*
|
|
102
|
+
* Link handling matches writeCodexPackageGuidance. Returns the file path when
|
|
103
|
+
* the file changed, and null otherwise.
|
|
105
104
|
*/
|
|
106
105
|
export function writeCodexAgentsGuidance(codexHome, packageGuidanceText, allPackageGuidancePaths) {
|
|
107
|
-
return
|
|
108
|
-
codexHome,
|
|
109
|
-
allPackageGuidancePaths,
|
|
110
|
-
currentText => withPackageGuidanceBlock(withSkillLoadBlock(currentText), packageGuidanceText),
|
|
111
|
-
);
|
|
106
|
+
return writeCodexPackageGuidance(codexHome, packageGuidanceText, allPackageGuidancePaths);
|
|
112
107
|
}
|
|
113
108
|
|
|
114
109
|
/**
|
|
115
110
|
* Keep CODEX_HOME/AGENTS.md carrying the package guidance, with no skill-load block.
|
|
116
111
|
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
112
|
+
* An existing skill-load block is removed. A symbolic link that points at the
|
|
113
|
+
* package guidance, the shared agents-home copy or the retired copy under the
|
|
114
|
+
* Claude home, is replaced by a regular file. A link to any other file is left
|
|
115
|
+
* alone.
|
|
116
|
+
*
|
|
117
|
+
* Returns the file path when the file changed, and null otherwise.
|
|
119
118
|
*/
|
|
120
119
|
export function writeCodexPackageGuidance(codexHome, packageGuidanceText, allPackageGuidancePaths) {
|
|
121
120
|
return writeCodexGuidanceFile(
|
|
@@ -10,11 +10,10 @@ import {
|
|
|
10
10
|
QUESTION_PRESENTATION_BLOCK_START,
|
|
11
11
|
SKILL_LOAD_BLOCK_END,
|
|
12
12
|
SKILL_LOAD_BLOCK_START,
|
|
13
|
-
SKILL_LOAD_INSTRUCTION,
|
|
14
13
|
removeCodexDeepenGuidance,
|
|
15
14
|
removeCodexPackageGuidance,
|
|
16
15
|
removeCodexQuestionGuidance,
|
|
17
|
-
|
|
16
|
+
withoutSkillLoadBlock,
|
|
18
17
|
DEEPEN_GUIDANCE_BLOCK_END,
|
|
19
18
|
DEEPEN_GUIDANCE_BLOCK_START,
|
|
20
19
|
DEEPEN_GUIDANCE_TEXT,
|
|
@@ -24,7 +23,10 @@ import {
|
|
|
24
23
|
writeCodexQuestionGuidance,
|
|
25
24
|
} from './codex-skill-load-block.mjs';
|
|
26
25
|
|
|
27
|
-
const
|
|
26
|
+
const STALE_SKILL_LOAD_SENTENCE = 'Load the pstack:poteto-mode skill before your first tool call or reply: '
|
|
27
|
+
+ 'open its SKILL.md from your skills list and follow it. This binds every Codex agent, '
|
|
28
|
+
+ 'including a helper started with spawn_agent, whatever its task message says.';
|
|
29
|
+
const STALE_SKILL_LOAD_BLOCK = `${SKILL_LOAD_BLOCK_START}\n${STALE_SKILL_LOAD_SENTENCE}\n${SKILL_LOAD_BLOCK_END}\n`;
|
|
28
30
|
const PACKAGE_GUIDANCE = 'Status\n\nChanged / proof / blocked.\n';
|
|
29
31
|
const QUESTION_POLICY = '# Present questions clearly\n\nAsk one question.\n';
|
|
30
32
|
|
|
@@ -52,34 +54,44 @@ function writeGuidance(homes) {
|
|
|
52
54
|
return writeCodexAgentsGuidance(homes.codexHome, PACKAGE_GUIDANCE, homes.allPackageGuidancePaths);
|
|
53
55
|
}
|
|
54
56
|
|
|
55
|
-
test('
|
|
56
|
-
assert.
|
|
57
|
-
assert.
|
|
57
|
+
test('a complete skill-load block is removed and the lines around it stay', () => {
|
|
58
|
+
assert.equal(withoutSkillLoadBlock(`Intro\n${STALE_SKILL_LOAD_BLOCK}Outro\n`), 'Intro\nOutro\n');
|
|
59
|
+
assert.equal(withoutSkillLoadBlock(`${STALE_SKILL_LOAD_BLOCK}\nNotes\n`), 'Notes\n');
|
|
60
|
+
assert.equal(
|
|
61
|
+
withoutSkillLoadBlock(`${STALE_SKILL_LOAD_BLOCK}\n${STALE_SKILL_LOAD_BLOCK}\nNotes\n`),
|
|
62
|
+
'Notes\n',
|
|
63
|
+
);
|
|
64
|
+
assert.equal(
|
|
65
|
+
withoutSkillLoadBlock(`${SKILL_LOAD_BLOCK_START}\nHalf a block\n`),
|
|
66
|
+
`${SKILL_LOAD_BLOCK_START}\nHalf a block\n`,
|
|
67
|
+
);
|
|
58
68
|
});
|
|
59
69
|
|
|
60
|
-
test('a missing Codex guidance file is created holding the
|
|
70
|
+
test('a missing Codex guidance file is created holding the package guidance', (context) => {
|
|
61
71
|
const homes = makeHomes(context);
|
|
62
72
|
|
|
63
73
|
const writtenPath = writeGuidance(homes);
|
|
64
74
|
|
|
65
75
|
assert.equal(writtenPath, homes.agentsPath);
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
);
|
|
76
|
+
const rendered = readFileSync(homes.agentsPath, 'utf8');
|
|
77
|
+
assert.equal(rendered, guidanceBlock(PACKAGE_GUIDANCE));
|
|
78
|
+
assert.equal(rendered.includes('pstack:poteto-mode'), false);
|
|
79
|
+
assert.equal(rendered.includes(SKILL_LOAD_BLOCK_START), false);
|
|
70
80
|
});
|
|
71
81
|
|
|
72
|
-
test('existing guidance keeps
|
|
82
|
+
test('existing guidance keeps its lines and gains the package guidance', (context) => {
|
|
73
83
|
const homes = makeHomes(context);
|
|
74
84
|
mkdirSync(homes.codexHome);
|
|
75
85
|
writeFileSync(homes.agentsPath, '# pstack model configuration\n\nbug-fix: gpt-6-astra\n');
|
|
76
86
|
|
|
77
87
|
writeGuidance(homes);
|
|
78
88
|
|
|
89
|
+
const rendered = readFileSync(homes.agentsPath, 'utf8');
|
|
79
90
|
assert.equal(
|
|
80
|
-
|
|
81
|
-
|
|
91
|
+
rendered,
|
|
92
|
+
`# pstack model configuration\n\nbug-fix: gpt-6-astra\n\n${guidanceBlock(PACKAGE_GUIDANCE)}`,
|
|
82
93
|
);
|
|
94
|
+
assert.equal(rendered.includes('pstack:poteto-mode'), false);
|
|
83
95
|
});
|
|
84
96
|
|
|
85
97
|
test('a repeat install leaves the file unchanged and reports no write', (context) => {
|
|
@@ -93,20 +105,43 @@ test('a repeat install leaves the file unchanged and reports no write', (context
|
|
|
93
105
|
assert.equal(readFileSync(homes.agentsPath, 'utf8'), afterFirstInstall);
|
|
94
106
|
});
|
|
95
107
|
|
|
96
|
-
test('
|
|
97
|
-
const
|
|
108
|
+
test('a stale skill-load sentence is removed and question and deepen blocks stay', (context) => {
|
|
109
|
+
const homes = makeHomes(context);
|
|
110
|
+
mkdirSync(homes.codexHome);
|
|
111
|
+
const questionBlock = `${QUESTION_PRESENTATION_BLOCK_START}\n${QUESTION_POLICY}${QUESTION_PRESENTATION_BLOCK_END}\n`;
|
|
112
|
+
const deepenBlock = `${DEEPEN_GUIDANCE_BLOCK_START}\n${DEEPEN_GUIDANCE_TEXT}\n${DEEPEN_GUIDANCE_BLOCK_END}\n`;
|
|
113
|
+
writeFileSync(
|
|
114
|
+
homes.agentsPath,
|
|
115
|
+
`${STALE_SKILL_LOAD_BLOCK}\n${guidanceBlock('Old guidance\n')}${questionBlock}${deepenBlock}`,
|
|
116
|
+
);
|
|
98
117
|
|
|
99
|
-
|
|
118
|
+
writeGuidance(homes);
|
|
119
|
+
|
|
120
|
+
const rendered = readFileSync(homes.agentsPath, 'utf8');
|
|
121
|
+
assert.equal(rendered.includes(STALE_SKILL_LOAD_SENTENCE), false);
|
|
122
|
+
assert.equal(rendered.includes('pstack:poteto-mode'), false);
|
|
123
|
+
assert.equal(rendered.includes(SKILL_LOAD_BLOCK_START), false);
|
|
124
|
+
assert.ok(rendered.includes(guidanceBlock(PACKAGE_GUIDANCE)));
|
|
125
|
+
assert.ok(rendered.includes(questionBlock));
|
|
126
|
+
assert.ok(rendered.includes(deepenBlock));
|
|
100
127
|
});
|
|
101
128
|
|
|
102
|
-
test('a start marker with no end marker
|
|
129
|
+
test('a start marker with no end marker stays and no skill name is added', (context) => {
|
|
130
|
+
const homes = makeHomes(context);
|
|
103
131
|
const broken = `${SKILL_LOAD_BLOCK_START}\nHalf a block\n`;
|
|
132
|
+
mkdirSync(homes.codexHome);
|
|
133
|
+
writeFileSync(homes.agentsPath, broken);
|
|
134
|
+
|
|
135
|
+
writeGuidance(homes);
|
|
104
136
|
|
|
105
|
-
|
|
137
|
+
const rendered = readFileSync(homes.agentsPath, 'utf8');
|
|
138
|
+
assert.ok(rendered.startsWith(broken));
|
|
139
|
+
assert.equal(rendered.includes('pstack:poteto-mode'), false);
|
|
140
|
+
assert.ok(rendered.includes(PACKAGE_GUIDANCE_BLOCK_START));
|
|
106
141
|
});
|
|
107
142
|
|
|
108
143
|
for (const linkName of ['retiredGuidancePath', 'sharedGuidancePath']) {
|
|
109
|
-
test(`a link to package guidance at ${linkName} becomes a file with
|
|
144
|
+
test(`a link to package guidance at ${linkName} becomes a file with the package block`, (context) => {
|
|
110
145
|
const homes = makeHomes(context);
|
|
111
146
|
mkdirSync(homes.codexHome);
|
|
112
147
|
symlinkSync(homes[linkName], homes.agentsPath);
|
|
@@ -115,24 +150,21 @@ for (const linkName of ['retiredGuidancePath', 'sharedGuidancePath']) {
|
|
|
115
150
|
|
|
116
151
|
assert.equal(writtenPath, homes.agentsPath);
|
|
117
152
|
assert.equal(lstatSync(homes.agentsPath).isSymbolicLink(), false);
|
|
118
|
-
assert.equal(
|
|
119
|
-
|
|
120
|
-
`${EXPECTED_BLOCK}\n${guidanceBlock(PACKAGE_GUIDANCE)}`,
|
|
121
|
-
);
|
|
153
|
+
assert.equal(readFileSync(homes.agentsPath, 'utf8'), guidanceBlock(PACKAGE_GUIDANCE));
|
|
154
|
+
assert.equal(readFileSync(homes.agentsPath, 'utf8').includes('pstack:poteto-mode'), false);
|
|
122
155
|
});
|
|
123
156
|
}
|
|
124
157
|
|
|
125
|
-
test('a converted file takes the new package guidance
|
|
158
|
+
test('a converted file takes the new package guidance and drops a stale skill-load block', (context) => {
|
|
126
159
|
const homes = makeHomes(context);
|
|
127
160
|
mkdirSync(homes.codexHome);
|
|
128
|
-
writeFileSync(homes.agentsPath, `${
|
|
161
|
+
writeFileSync(homes.agentsPath, `${STALE_SKILL_LOAD_BLOCK}\n${guidanceBlock('Old guidance\n')}`);
|
|
129
162
|
|
|
130
163
|
writeGuidance(homes);
|
|
131
164
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
);
|
|
165
|
+
const rendered = readFileSync(homes.agentsPath, 'utf8');
|
|
166
|
+
assert.equal(rendered, guidanceBlock(PACKAGE_GUIDANCE));
|
|
167
|
+
assert.equal(rendered.includes('pstack:poteto-mode'), false);
|
|
136
168
|
});
|
|
137
169
|
|
|
138
170
|
test('a link to a file the package does not own is left alone', (context) => {
|
|
@@ -32,6 +32,7 @@ function installInScratchHome(homeDirectory, argumentsList = [], environment = {
|
|
|
32
32
|
CLAUDE_CONFIG_DIR: undefined,
|
|
33
33
|
LLM_SETTINGS_PROFILES_ROOT: undefined,
|
|
34
34
|
CDE_INSTALL_PSTACK: '0',
|
|
35
|
+
CDE_INSTALL_DEEPSTACK: '0',
|
|
35
36
|
CDE_INSTALL_USAGE_WRAPUP: '0',
|
|
36
37
|
CDE_INSTALL_SUBAGENT_MODELS: '0',
|
|
37
38
|
CDE_INSTALL_DEEPEN_FIRST_PROMPT: '0',
|