claude-dev-env 8.89.0 → 8.90.1
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/pr-visual/README.md +29 -0
- package/.agents/skills/pr-visual/SKILL.md +62 -0
- package/.agents/skills/pr-visual/assets/icons.symbols +27 -0
- package/.agents/skills/pr-visual/assets/viewer.html +147 -0
- package/.agents/skills/pr-visual/fixtures/bar.scene.json +193 -0
- package/.agents/skills/pr-visual/fixtures/flow.scene.json +201 -0
- package/.agents/skills/pr-visual/fixtures/head/metrics.json +16 -0
- package/.agents/skills/pr-visual/fixtures/integrated.scene.json +239 -0
- package/.agents/skills/pr-visual/fixtures/line.scene.json +193 -0
- package/.agents/skills/pr-visual/fixtures/scatter.scene.json +193 -0
- package/.agents/skills/pr-visual/fixtures/snapshot.json +21 -0
- package/.agents/skills/pr-visual/references/evidence.md +9 -0
- package/.agents/skills/pr-visual/references/formats.md +30 -0
- package/.agents/skills/pr-visual/references/scene-contract.md +48 -0
- package/.agents/skills/pr-visual/references/visual-contract.md +23 -0
- package/.agents/skills/pr-visual/scripts/collect.py +111 -0
- package/.agents/skills/pr-visual/scripts/metrics.py +99 -0
- package/.agents/skills/pr-visual/scripts/render.py +133 -0
- package/.agents/skills/pr-visual/scripts/test_collect.py +58 -0
- package/.agents/skills/pr-visual/scripts/test_metrics.py +47 -0
- package/.agents/skills/pr-visual/scripts/test_render.py +77 -0
- package/.agents/skills/pr-visual/scripts/verify.py +218 -0
- package/.agents/skills/pr-visual/scripts/visual_config/config/constants.py +104 -0
- package/bin/codex-skill-load-block.mjs +0 -25
- package/bin/codex-skill-load-block.test.mjs +5 -17
- package/bin/codex-trimmed-sol.test.mjs +0 -1
- package/bin/cursor-native-hooks.test.mjs +3 -3
- package/bin/ever-shipped-skills.mjs +1 -0
- package/bin/install-deepstack-plugin.mjs +42 -25
- package/bin/install-deepstack-plugin.test.mjs +37 -11
- package/bin/install-pstack-plugin.mjs +58 -11
- package/bin/install-pstack-plugin.test.mjs +54 -21
- package/bin/install.agents-home.test.mjs +0 -1
- package/bin/install.codex-rules.test.mjs +0 -2
- package/bin/install.cursor-rules.test.mjs +90 -26
- package/bin/install.mjs +30 -36
- package/bin/install.pr-visual.test.mjs +40 -0
- package/bin/install.prompt-context.test.mjs +3 -4
- package/bin/install.prune.test.mjs +0 -1
- package/bin/install.question-presentation.test.mjs +0 -4
- package/bin/install.settings-defaults.test.mjs +0 -1
- package/bin/install.shared-settings.test.mjs +0 -1
- package/bin/install.test.mjs +14 -44
- package/bin/install.transaction.test.mjs +0 -8
- package/bin/install.uninstall-transaction.test.mjs +0 -2
- package/bin/install.update-keeps-hooks.test.mjs +0 -1
- package/docs/BDD_SCENARIO_QUALITY.md +1 -1
- package/docs/BDD_TEST_LAYOUT.md +1 -1
- package/docs/CODE_RULES.md +1 -1
- package/docs/rule-guides/deepstack-wording.md +3 -5
- package/docs/rule-guides/session-title.md +5 -4
- package/hooks/blocking/session_title_turn_gate.py +287 -0
- package/hooks/blocking/test_session_title_format_gate.py +5 -0
- package/hooks/blocking/test_session_title_turn_gate.py +264 -0
- package/hooks/features/conduct-gates.md +3 -0
- package/hooks/features/session-context.md +0 -4
- package/hooks/hooks.json +5 -5
- package/hooks/hooks_constants/mod_handoff_constants.py +0 -2
- package/hooks/hooks_constants/session_title_constants.py +41 -3
- package/hooks/lifecycle/nested_project_hooks.py +2 -2
- package/hooks/lifecycle/test_nested_project_hooks.py +2 -2
- package/hooks/session/compaction_user_messages.py +2 -2
- package/package.json +2 -2
- package/rules/deepstack-wording.md +3 -3
- package/scripts/ci/install-playtest.mjs +58 -314
- package/scripts/ci/install-playtest.test.mjs +24 -6
- package/scripts/policy_lint/adapter_configuration.py +1 -0
- package/scripts/policy_lint/config/constants.py +1 -0
- package/scripts/test_deepstack_review_criterion.py +2 -3
- package/scripts/test_deepstack_wording_rule.py +6 -4
- package/scripts/tests/test_adapter_configuration.py +8 -0
- package/.agents/skills/deepen/SKILL.md +0 -26
- package/commands/deepen.md +0 -14
- package/docs/decisions/deepstack-gate.md +0 -32
- package/hooks/hooks_constants/deepen_first_prompt_constants.py +0 -187
- package/hooks/session/deepen_default_rewrite.py +0 -128
- package/hooks/session/deepen_first_prompt.py +0 -383
- package/hooks/session/deepen_role_gate.py +0 -171
- package/hooks/session/deepen_turn_marker.py +0 -75
- package/hooks/session/fixtures/deepstack_replay.json +0 -15
- package/hooks/session/grade_deepstack_replay.py +0 -78
- package/hooks/session/test_deepen_default_rewrite.py +0 -90
- package/hooks/session/test_deepen_first_prompt.py +0 -452
- package/hooks/session/test_deepen_role_gate.py +0 -83
- package/hooks/session/test_deepen_turn_marker.py +0 -59
- package/hooks/session/test_grade_deepstack_replay.py +0 -47
|
@@ -36,7 +36,6 @@ function runInstaller(homeDirectory, extraArguments, environmentOverrides = {})
|
|
|
36
36
|
CDE_INSTALL_DEEPSTACK: '0',
|
|
37
37
|
CDE_INSTALL_USAGE_WRAPUP: '0',
|
|
38
38
|
CDE_INSTALL_SUBAGENT_MODELS: '0',
|
|
39
|
-
CDE_INSTALL_DEEPEN_FIRST_PROMPT: '0',
|
|
40
39
|
HOME: homeDirectory,
|
|
41
40
|
USERPROFILE: homeDirectory,
|
|
42
41
|
CODEX_HOME: join(homeDirectory, '.codex'),
|
|
@@ -144,10 +143,11 @@ test('seeds one editable policy and one native Codex routing hook', () => {
|
|
|
144
143
|
const allCodexStopCommands = firstCodexHooks.hooks.Stop.flatMap(group => group.hooks.map(hook => hook.command));
|
|
145
144
|
assert.equal(allCodexStopCommands.length, 1, 'Codex gets the open tasks Stop gate and no other Stop hook');
|
|
146
145
|
assert.match(allCodexStopCommands[0], /open_tasks_stop_gate\.py/);
|
|
147
|
-
assert.
|
|
148
|
-
firstCodexHooks.hooks.UserPromptSubmit.some(group =>
|
|
146
|
+
assert.equal(
|
|
147
|
+
(firstCodexHooks.hooks.UserPromptSubmit ?? []).some(group =>
|
|
149
148
|
group.hooks.some(hook => String(hook.command).includes('deepen_first_prompt.py')),
|
|
150
149
|
),
|
|
150
|
+
false,
|
|
151
151
|
);
|
|
152
152
|
assert.equal(
|
|
153
153
|
firstCodexHooks.hooks.PreToolUse.some(group => group.matcher === 'Write|Edit|MultiEdit|apply_patch'),
|
|
@@ -290,6 +290,7 @@ test('Codex reinstall removes omitted package hooks and preserves user configura
|
|
|
290
290
|
const suffixCommand = `python "${join(codexHome, 'hooks', 'blocking', 'pre_tool_use_dispatcher.py.backup')}"`;
|
|
291
291
|
hooksByEvent.PreToolUse.push({ matcher: 'suffix', hooks: [{ type: 'command', command: suffixCommand }] });
|
|
292
292
|
hooksByEvent.CustomEvent = { metadata: 'keep-event' };
|
|
293
|
+
hooksByEvent.PostToolUse = [{ matcher: 'Write', hooks: [{ type: 'command', command: 'python user-post-tool-use.py' }] }];
|
|
293
294
|
const expectedUserHooks = JSON.parse(JSON.stringify(hooksByEvent));
|
|
294
295
|
for (const eachGroup of Object.values(expectedUserHooks).flat()) {
|
|
295
296
|
if (eachGroup.hooks && eachGroup.metadata) eachGroup.hooks.shift();
|
|
@@ -306,21 +307,20 @@ test('Codex reinstall removes omitted package hooks and preserves user configura
|
|
|
306
307
|
['Agent|Task', 'multi_agent_v1__spawn_agent'].includes(eachGroup.matcher));
|
|
307
308
|
assert.equal(allSpawnGroups.length, 2);
|
|
308
309
|
migratedHooks.hooks.PreToolUse = migratedHooks.hooks.PreToolUse.filter(eachGroup => !allSpawnGroups.includes(eachGroup));
|
|
309
|
-
const allPackagePromptCommands = migratedHooks.hooks.UserPromptSubmit
|
|
310
|
+
const allPackagePromptCommands = (migratedHooks.hooks.UserPromptSubmit ?? [])
|
|
310
311
|
.flatMap(eachGroup => eachGroup.hooks)
|
|
311
312
|
.map(eachHook => eachHook.command);
|
|
312
|
-
assert.
|
|
313
|
+
assert.equal(allPackagePromptCommands.some(eachCommand => eachCommand.includes('deepen_first_prompt.py')), false);
|
|
313
314
|
delete migratedHooks.hooks.UserPromptSubmit;
|
|
314
315
|
const allPackageStopGroups = migratedHooks.hooks.Stop.filter(eachGroup =>
|
|
315
316
|
eachGroup.hooks.some(eachHook => eachHook.command.includes('open_tasks_stop_gate.py')));
|
|
316
317
|
assert.equal(allPackageStopGroups.length, 1);
|
|
317
318
|
migratedHooks.hooks.Stop = migratedHooks.hooks.Stop.filter(eachGroup => !allPackageStopGroups.includes(eachGroup));
|
|
318
|
-
const
|
|
319
|
-
.
|
|
320
|
-
|
|
321
|
-
assert.equal(
|
|
322
|
-
|
|
323
|
-
delete migratedHooks.hooks.PostToolUse;
|
|
319
|
+
const allPackagePostToolUseGroups = migratedHooks.hooks.PostToolUse.filter(eachGroup =>
|
|
320
|
+
eachGroup.hooks.some(eachHook => eachHook.command.includes('owned_pr_review_loop.py')));
|
|
321
|
+
assert.equal(allPackagePostToolUseGroups.length, 1);
|
|
322
|
+
assert.equal(allPackagePostToolUseGroups[0].hooks.length, 1);
|
|
323
|
+
migratedHooks.hooks.PostToolUse = migratedHooks.hooks.PostToolUse.filter(eachGroup => !allPackagePostToolUseGroups.includes(eachGroup));
|
|
324
324
|
assert.deepEqual(migratedHooks, { enabled: false, metadata: 'keep-file', hooks: expectedUserHooks });
|
|
325
325
|
assert.equal(readFileSync(configPath, 'utf8'), disabledConfig);
|
|
326
326
|
const firstInstall = readFileSync(codexHooksPath, 'utf8');
|
|
@@ -367,27 +367,91 @@ test('uninstall removes the routing hook and keeps a Codex user hook', () => {
|
|
|
367
367
|
}
|
|
368
368
|
});
|
|
369
369
|
|
|
370
|
-
|
|
371
|
-
|
|
370
|
+
const RETIRED_DEEPEN_CODEX_BLOCK = '<!-- claude-dev-env deepen: start -->\nDeepen the first interactive user task.\n<!-- claude-dev-env deepen: end -->\n';
|
|
371
|
+
|
|
372
|
+
function allSettingsHookCommands(settingsPath) {
|
|
373
|
+
const settings = JSON.parse(readFileSync(settingsPath, 'utf8'));
|
|
374
|
+
return Object.values(settings.hooks ?? {})
|
|
375
|
+
.flatMap(allGroups => allGroups)
|
|
376
|
+
.flatMap(group => group.hooks ?? [])
|
|
377
|
+
.map(hook => String(hook.command));
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function assertNoDeepenPieceInstalled(homeDirectory, cursorSkillPath, codexGuidancePath, settingsPath) {
|
|
381
|
+
assert.equal(existsSync(cursorSkillPath), false, 'the Cursor deepen skill is gone');
|
|
382
|
+
assert.equal(existsSync(join(homeDirectory, '.claude', 'skills', 'deepen')), false, 'the Claude deepen skill is gone');
|
|
383
|
+
assert.equal(existsSync(join(homeDirectory, '.claude', 'commands', 'deepen.md')), false, 'the /deepen command is gone');
|
|
384
|
+
const codexGuidance = existsSync(codexGuidancePath) ? readFileSync(codexGuidancePath, 'utf8') : '';
|
|
385
|
+
assert.doesNotMatch(codexGuidance, /claude-dev-env deepen/);
|
|
386
|
+
assert.equal(
|
|
387
|
+
allSettingsHookCommands(settingsPath).some(command => command.includes('deepen_first_prompt.py')),
|
|
388
|
+
false,
|
|
389
|
+
'settings.json runs no deepen hook',
|
|
390
|
+
);
|
|
391
|
+
const cursorHooksPath = join(homeDirectory, DEFAULT_CURSOR_DIRECTORY_NAME, 'hooks.json');
|
|
392
|
+
const cursorHooksText = existsSync(cursorHooksPath) ? readFileSync(cursorHooksPath, 'utf8') : '';
|
|
393
|
+
assert.equal(cursorHooksText.includes('deepen_first_prompt.py'), false, 'Cursor runs no deepen hook');
|
|
394
|
+
const installedHookPath = join(homeDirectory, '.claude', 'hooks', 'session', 'deepen_first_prompt.py');
|
|
395
|
+
if (existsSync(installedHookPath)) {
|
|
396
|
+
assert.match(readFileSync(installedHookPath, 'utf8'), /claude-dev-env retired hook stand-in/);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
test('a full install over an old deepen install removes every deepen piece, and a repeat install keeps them out', () => {
|
|
401
|
+
const homeDirectory = mkdtempSync(join(tmpdir(), 'cdev-deepen-retired-'));
|
|
372
402
|
try {
|
|
373
|
-
const
|
|
403
|
+
const claudeDirectory = join(homeDirectory, '.claude');
|
|
374
404
|
const cursorSkillPath = join(homeDirectory, DEFAULT_CURSOR_DIRECTORY_NAME, 'skills', 'deepen', 'SKILL.md');
|
|
375
405
|
const codexGuidancePath = join(homeDirectory, '.codex', 'AGENTS.md');
|
|
376
|
-
const manifestPath = join(
|
|
406
|
+
const manifestPath = join(claudeDirectory, '.claude-dev-env-manifest.json');
|
|
407
|
+
const settingsPath = join(claudeDirectory, 'settings.json');
|
|
408
|
+
const retiredPluginVersion = join(claudeDirectory, 'plugins', 'cache', 'claude-dev-env', 'deepen-first-prompt', '0.1.0');
|
|
409
|
+
const fakeClaudeLogPath = join(homeDirectory, 'fake-claude.log');
|
|
410
|
+
const orphanedMarkerPath = join(retiredPluginVersion, '.orphaned_at');
|
|
411
|
+
const isWindows = process.platform === 'win32';
|
|
412
|
+
const fakeClaudePath = join(homeDirectory, isWindows ? 'fake-claude.cmd' : 'fake-claude.sh');
|
|
413
|
+
writeFileSync(
|
|
414
|
+
fakeClaudePath,
|
|
415
|
+
isWindows
|
|
416
|
+
? `@echo off\r\n>>"${fakeClaudeLogPath}" echo %*\r\ntype nul > "${orphanedMarkerPath}"\r\n`
|
|
417
|
+
: `#!/bin/sh\necho "$@" >> '${fakeClaudeLogPath}'\ntouch '${orphanedMarkerPath}'\n`,
|
|
418
|
+
{ mode: 0o755 },
|
|
419
|
+
);
|
|
420
|
+
const environment = { CDE_CLAUDE_EXECUTABLE: fakeClaudePath };
|
|
377
421
|
|
|
378
|
-
runInstaller(homeDirectory, []);
|
|
422
|
+
runInstaller(homeDirectory, [], environment);
|
|
423
|
+
|
|
424
|
+
mkdirSync(dirname(cursorSkillPath), { recursive: true });
|
|
425
|
+
writeFileSync(cursorSkillPath, '# deepen\n');
|
|
426
|
+
const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
|
|
427
|
+
manifest.files.push(cursorSkillPath);
|
|
428
|
+
writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
|
|
429
|
+
writeFileSync(codexGuidancePath, `${readFileSync(codexGuidancePath, 'utf8')}${RETIRED_DEEPEN_CODEX_BLOCK}`);
|
|
430
|
+
const settings = JSON.parse(readFileSync(settingsPath, 'utf8'));
|
|
431
|
+
const reminderCommand = settings.hooks.UserPromptSubmit
|
|
432
|
+
.flatMap(group => group.hooks)
|
|
433
|
+
.map(hook => hook.command)
|
|
434
|
+
.find(command => command.includes('skill_loaded_reminder.py'));
|
|
435
|
+
settings.hooks.UserPromptSubmit.push({
|
|
436
|
+
matcher: '',
|
|
437
|
+
hooks: [{ type: 'command', command: reminderCommand.replace('skill_loaded_reminder.py', 'deepen_first_prompt.py'), timeout: 10 }],
|
|
438
|
+
});
|
|
439
|
+
writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
|
|
440
|
+
const cursorHooksPath = join(homeDirectory, DEFAULT_CURSOR_DIRECTORY_NAME, 'hooks.json');
|
|
441
|
+
const cursorHooks = JSON.parse(readFileSync(cursorHooksPath, 'utf8'));
|
|
442
|
+
cursorHooks.hooks.beforeSubmitPrompt = [{ command: "python3 '/old/hooks/session/deepen_first_prompt.py'", timeout: 10 }];
|
|
443
|
+
writeFileSync(cursorHooksPath, JSON.stringify(cursorHooks, null, 2));
|
|
444
|
+
mkdirSync(retiredPluginVersion, { recursive: true });
|
|
379
445
|
|
|
380
|
-
|
|
381
|
-
assert.equal(JSON.parse(readFileSync(manifestPath, 'utf8')).files.includes(cursorSkillPath), true);
|
|
382
|
-
const codexGuidance = readFileSync(codexGuidancePath, 'utf8');
|
|
383
|
-
assert.match(codexGuidance, /<!-- claude-dev-env deepen: start -->/);
|
|
384
|
-
assert.match(codexGuidance, /CDE_DEEPEN:v1/);
|
|
446
|
+
runInstaller(homeDirectory, [], environment);
|
|
385
447
|
|
|
386
|
-
|
|
448
|
+
assertNoDeepenPieceInstalled(homeDirectory, cursorSkillPath, codexGuidancePath, settingsPath);
|
|
449
|
+
assert.match(readFileSync(fakeClaudeLogPath, 'utf8'), /^plugin uninstall deepen-first-prompt@claude-dev-env\r?$/m);
|
|
450
|
+
assert.equal(existsSync(retiredPluginVersion), false, 'the retired plugin cache is gone');
|
|
451
|
+
|
|
452
|
+
runInstaller(homeDirectory, [], environment);
|
|
387
453
|
|
|
388
|
-
|
|
389
|
-
const remainingGuidance = existsSync(codexGuidancePath) ? readFileSync(codexGuidancePath, 'utf8') : '';
|
|
390
|
-
assert.doesNotMatch(remainingGuidance, /claude-dev-env deepen/);
|
|
454
|
+
assertNoDeepenPieceInstalled(homeDirectory, cursorSkillPath, codexGuidancePath, settingsPath);
|
|
391
455
|
} finally {
|
|
392
456
|
rmSync(homeDirectory, { recursive: true, force: true });
|
|
393
457
|
}
|
|
@@ -579,7 +643,7 @@ test('a full install writes only native Cursor hooks: AGENTS.md on sessionStart
|
|
|
579
643
|
assert.equal(gateCommands.length, 1);
|
|
580
644
|
assert.deepEqual(cursorHooks.hooks.stop.filter(entry => entry.command === userStopEntry.command), [userStopEntry]);
|
|
581
645
|
assert.equal(execSync(gateCommands[0], { input: '{"conversation_id": "c-1", "status": "completed"}', encoding: 'utf8' }), '');
|
|
582
|
-
assert.
|
|
646
|
+
assert.equal(cursorHooks.hooks.beforeSubmitPrompt, undefined);
|
|
583
647
|
|
|
584
648
|
runInstaller(homeDirectory, ['--uninstall']);
|
|
585
649
|
const remainingHooks = JSON.parse(readFileSync(cursorHooksPath, 'utf8'));
|
package/bin/install.mjs
CHANGED
|
@@ -55,17 +55,19 @@ import {
|
|
|
55
55
|
ALL_CODEX_NATIVE_POST_TOOL_USE_HOOK_SCRIPT_NAMES,
|
|
56
56
|
} from './install-constants.mjs';
|
|
57
57
|
import {
|
|
58
|
+
ALL_RETIRED_PLUGIN_SPECS,
|
|
58
59
|
PSTACK_PLUGIN_SPEC,
|
|
59
|
-
DEEPEN_FIRST_PROMPT_PLUGIN_SPEC,
|
|
60
60
|
SUBAGENT_MODELS_PLUGIN_SPEC,
|
|
61
61
|
USAGE_WRAPUP_PLUGIN_SPEC,
|
|
62
62
|
installMarketplacePlugins,
|
|
63
|
+
removeRetiredMarketplacePlugins,
|
|
63
64
|
shouldInstallMarketplacePlugin,
|
|
64
65
|
} from './install-pstack-plugin.mjs';
|
|
65
66
|
import {
|
|
66
67
|
DEEPSTACK_PLUGIN_SPEC,
|
|
67
68
|
installDeepStackForHosts,
|
|
68
69
|
removeDeepStackCursorHooks,
|
|
70
|
+
removeRetiredCursorFirstTaskHook,
|
|
69
71
|
} from './install-deepstack-plugin.mjs';
|
|
70
72
|
import { seedCodexPstackModels } from './seed-codex-pstack-models.mjs';
|
|
71
73
|
import {
|
|
@@ -86,7 +88,6 @@ import {
|
|
|
86
88
|
removeCodexPackageGuidance,
|
|
87
89
|
removeCodexQuestionGuidance,
|
|
88
90
|
writeCodexAgentsGuidance,
|
|
89
|
-
writeCodexDeepenGuidance,
|
|
90
91
|
writeCodexPackageGuidance,
|
|
91
92
|
writeCodexQuestionGuidance,
|
|
92
93
|
} from './codex-skill-load-block.mjs';
|
|
@@ -248,8 +249,8 @@ export const CORE_SKILLS = [
|
|
|
248
249
|
'issue-tracker',
|
|
249
250
|
'task-build',
|
|
250
251
|
'eli5',
|
|
251
|
-
'deepen',
|
|
252
252
|
'efficient-review',
|
|
253
|
+
'pr-visual',
|
|
253
254
|
];
|
|
254
255
|
|
|
255
256
|
export function collectPackageSourceConflicts(packageDirectory) {
|
|
@@ -1299,6 +1300,7 @@ export const RETIRED_HOOK_REGISTRATION_RELATIVE_PATHS = new Set([
|
|
|
1299
1300
|
'blocking/artifact_look_gate.py',
|
|
1300
1301
|
'blocking/session_title_stop_gate.py',
|
|
1301
1302
|
'session/issue_tracker_session_starter.py',
|
|
1303
|
+
'session/deepen_first_prompt.py',
|
|
1302
1304
|
]);
|
|
1303
1305
|
|
|
1304
1306
|
export const MOVED_HOOK_RELATIVE_PATHS = new Set([
|
|
@@ -1998,7 +2000,7 @@ export function pruneRetiredHookEntriesFromSettings(
|
|
|
1998
2000
|
return removedCount;
|
|
1999
2001
|
}
|
|
2000
2002
|
|
|
2001
|
-
const RETIRED_HOOK_STAND_IN_MARKER = 'claude-dev-env retired hook stand-in';
|
|
2003
|
+
export const RETIRED_HOOK_STAND_IN_MARKER = 'claude-dev-env retired hook stand-in';
|
|
2002
2004
|
|
|
2003
2005
|
const RETIRED_HOOK_STAND_IN_SOURCE = `"""${RETIRED_HOOK_STAND_IN_MARKER}.
|
|
2004
2006
|
|
|
@@ -2571,10 +2573,12 @@ function pruneStaleFilesAcrossManagedRoots(priorInstalledFiles, currentInstalled
|
|
|
2571
2573
|
}
|
|
2572
2574
|
const cursorInstallDirectory = INSTALL_ROOT_RESOLUTION.cursorInstallDirectory;
|
|
2573
2575
|
const retiredCursorSyncManifestPath = join(cursorInstallDirectory, '.sync-manifest.json');
|
|
2574
|
-
const
|
|
2575
|
-
|
|
2576
|
+
const retiredCursorSkillsDirectory = join(cursorInstallDirectory, MANAGED_SKILLS_DIRECTORY_NAME);
|
|
2577
|
+
const isRetiredCursorFile = priorFile => resolve(priorFile) === retiredCursorSyncManifestPath
|
|
2578
|
+
|| isInsideDirectory(resolve(priorFile), INSTALL_ROOT_RESOLUTION.cursorRulesInstallDirectory)
|
|
2579
|
+
|| isInsideDirectory(resolve(priorFile), retiredCursorSkillsDirectory);
|
|
2576
2580
|
const retiredCursorRulesOutcome = pruneStaleInstalledFiles(
|
|
2577
|
-
priorInstalledFiles?.filter(
|
|
2581
|
+
priorInstalledFiles?.filter(isRetiredCursorFile) ?? null,
|
|
2578
2582
|
currentInstalledFiles,
|
|
2579
2583
|
cursorInstallDirectory,
|
|
2580
2584
|
join(backupRoot, 'cursor'),
|
|
@@ -3015,23 +3019,6 @@ function executeInstallPlanMutations(plan, transactionHelpers) {
|
|
|
3015
3019
|
}
|
|
3016
3020
|
summary.skills = { created: skillsCreated, updated: skillsUpdated, pruned: 0, paths: skillPaths };
|
|
3017
3021
|
allInstalledFiles.push(...skillPaths);
|
|
3018
|
-
if (copiedSkillNames.has('deepen')) {
|
|
3019
|
-
for (const sourceRoot of allSourceRoots) {
|
|
3020
|
-
const deepenSourceDirectory = join(
|
|
3021
|
-
resolvePackageManagedDirectory(sourceRoot, MANAGED_SKILLS_DIRECTORY_NAME),
|
|
3022
|
-
'deepen',
|
|
3023
|
-
);
|
|
3024
|
-
if (!existsSync(deepenSourceDirectory)) continue;
|
|
3025
|
-
const cursorDeepenDirectory = join(
|
|
3026
|
-
INSTALL_ROOT_RESOLUTION.cursorInstallDirectory,
|
|
3027
|
-
'skills',
|
|
3028
|
-
'deepen',
|
|
3029
|
-
);
|
|
3030
|
-
const cursorSkillStats = copyTree(deepenSourceDirectory, cursorDeepenDirectory);
|
|
3031
|
-
allInstalledFiles.push(...cursorSkillStats.paths);
|
|
3032
|
-
break;
|
|
3033
|
-
}
|
|
3034
|
-
}
|
|
3035
3022
|
if (!selectedGroups) {
|
|
3036
3023
|
summary.retiredSessionContinuity = {
|
|
3037
3024
|
changedPaths: removeRetiredSessionContinuityHooks(),
|
|
@@ -3088,12 +3075,6 @@ function executeInstallPlanMutations(plan, transactionHelpers) {
|
|
|
3088
3075
|
claudeRoot: CLAUDE_HOME,
|
|
3089
3076
|
codexHome: INSTALL_ROOT_RESOLUTION.codexHomeDirectory,
|
|
3090
3077
|
cursorDirectory: INSTALL_ROOT_RESOLUTION.cursorInstallDirectory,
|
|
3091
|
-
firstTaskHookPath: join(
|
|
3092
|
-
INSTALL_ROOT_RESOLUTION.hooksInstallDirectory,
|
|
3093
|
-
'session',
|
|
3094
|
-
'deepen_first_prompt.py',
|
|
3095
|
-
),
|
|
3096
|
-
pythonCommand,
|
|
3097
3078
|
formatPathArgument: commandArgumentFromPath,
|
|
3098
3079
|
});
|
|
3099
3080
|
summary.deepStackPlugin = deepStackPlugin;
|
|
@@ -3106,10 +3087,27 @@ function executeInstallPlanMutations(plan, transactionHelpers) {
|
|
|
3106
3087
|
allActionNeededLines.push(`DeepStack (cursor): failed — ${cursorOutcome.warning}`);
|
|
3107
3088
|
}
|
|
3108
3089
|
}
|
|
3090
|
+
if (!selectedGroups) {
|
|
3091
|
+
const retiredCursorHooksPath = removeRetiredCursorFirstTaskHook(
|
|
3092
|
+
INSTALL_ROOT_RESOLUTION.cursorInstallDirectory,
|
|
3093
|
+
);
|
|
3094
|
+
if (retiredCursorHooksPath) {
|
|
3095
|
+
console.log(` Retired first-task hook removed from ${retiredCursorHooksPath}`);
|
|
3096
|
+
}
|
|
3097
|
+
const retiredPlugins = removeRetiredMarketplacePlugins(ALL_RETIRED_PLUGIN_SPECS, {
|
|
3098
|
+
claudeRoot: CLAUDE_HOME,
|
|
3099
|
+
});
|
|
3100
|
+
summary.retiredPlugins = retiredPlugins;
|
|
3101
|
+
for (const eachIdentifier of retiredPlugins.removedIdentifiers) {
|
|
3102
|
+
console.log(` Retired plugin uninstalled: ${eachIdentifier}`);
|
|
3103
|
+
}
|
|
3104
|
+
for (const eachFailure of retiredPlugins.failedRemovals) {
|
|
3105
|
+
console.warn(` Warning: retired plugin ${eachFailure.pluginIdentifier} was not uninstalled: ${eachFailure.detail}`);
|
|
3106
|
+
}
|
|
3107
|
+
}
|
|
3109
3108
|
const allRepositoryPluginSpecs = [
|
|
3110
3109
|
USAGE_WRAPUP_PLUGIN_SPEC,
|
|
3111
3110
|
SUBAGENT_MODELS_PLUGIN_SPEC,
|
|
3112
|
-
DEEPEN_FIRST_PROMPT_PLUGIN_SPEC,
|
|
3113
3111
|
].filter(eachSpec => shouldInstallMarketplacePlugin(eachSpec));
|
|
3114
3112
|
if (!selectedGroups && allRepositoryPluginSpecs.length > 0) {
|
|
3115
3113
|
const repositoryPlugins = installMarketplacePlugins(allRepositoryPluginSpecs, {
|
|
@@ -3133,10 +3131,7 @@ function executeInstallPlanMutations(plan, transactionHelpers) {
|
|
|
3133
3131
|
readFileSync(join(PACKAGE_ROOT, 'rules', 'question-presentation.md'), 'utf8'),
|
|
3134
3132
|
);
|
|
3135
3133
|
if (questionGuidancePath) allCodexGuidancePaths.push(questionGuidancePath);
|
|
3136
|
-
|
|
3137
|
-
INSTALL_ROOT_RESOLUTION.codexHomeDirectory,
|
|
3138
|
-
);
|
|
3139
|
-
if (deepenGuidancePath) allCodexGuidancePaths.push(deepenGuidancePath);
|
|
3134
|
+
removeCodexDeepenGuidance(INSTALL_ROOT_RESOLUTION.codexHomeDirectory);
|
|
3140
3135
|
const solPromptPath = installCodexSolPrompt(
|
|
3141
3136
|
INSTALL_ROOT_RESOLUTION.codexHomeDirectory,
|
|
3142
3137
|
join(PACKAGE_ROOT, SOL_PROMPT_DIRECTORY_NAME, SOL_PROMPT_FILE_NAME),
|
|
@@ -3686,7 +3681,6 @@ Usage:
|
|
|
3686
3681
|
npx ${PACKAGE_NAME} --no-deepstack Full install without the DeepStack plugin (also CDE_INSTALL_DEEPSTACK=0)
|
|
3687
3682
|
npx ${PACKAGE_NAME} --no-usage-wrapup Full install without the usage-wrapup plugin (also CDE_INSTALL_USAGE_WRAPUP=0)
|
|
3688
3683
|
npx ${PACKAGE_NAME} --no-subagent-models Full install without the subagent-models plugin (also CDE_INSTALL_SUBAGENT_MODELS=0)
|
|
3689
|
-
npx ${PACKAGE_NAME} --no-deepen-first-prompt Full install without the deepen-first-prompt plugin (also CDE_INSTALL_DEEPEN_FIRST_PROMPT=0)
|
|
3690
3684
|
npx ${PACKAGE_NAME} --uninstall Remove installed files from the selected root
|
|
3691
3685
|
npx ${PACKAGE_NAME} --help Show this help
|
|
3692
3686
|
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { strict as assert } from 'node:assert';
|
|
2
|
+
import { execFileSync } from 'node:child_process';
|
|
3
|
+
import { existsSync, mkdtempSync, readFileSync, realpathSync, rmSync } from 'node:fs';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { dirname, join } from 'node:path';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
import { test } from 'node:test';
|
|
8
|
+
|
|
9
|
+
const PACKAGE_DIRECTORY = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
10
|
+
const SKILL_PATH_SEGMENTS = ['.agents', 'skills', 'pr-visual'];
|
|
11
|
+
const REQUIRED_FILES = ['SKILL.md', 'scripts/collect.py', 'scripts/render.py', 'scripts/metrics.py', 'scripts/verify.py', 'scripts/visual_config/config/constants.py', 'assets/viewer.html', 'assets/icons.symbols', 'fixtures/bar.scene.json', 'fixtures/integrated.scene.json', 'references/formats.md'];
|
|
12
|
+
const INSTALL_MODES = [[], ['--only', 'core']];
|
|
13
|
+
const NPM_EXECUTABLE = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
14
|
+
|
|
15
|
+
test('packed full and core installs ship the runnable PR visual skill to the shared agents home', () => {
|
|
16
|
+
const sandbox = mkdtempSync(join(tmpdir(), 'cde-pr-visual-'));
|
|
17
|
+
try {
|
|
18
|
+
const packed = JSON.parse(execFileSync(NPM_EXECUTABLE, ['pack', '--json', '--ignore-scripts', '--offline', '--cache', join(sandbox, 'npm-cache'), '--pack-destination', sandbox], { cwd: PACKAGE_DIRECTORY, encoding: 'utf8', shell: process.platform === 'win32' }));
|
|
19
|
+
execFileSync('tar', ['-xzf', join(sandbox, packed[0].filename), '-C', sandbox]);
|
|
20
|
+
const packaged = join(sandbox, 'package');
|
|
21
|
+
for (const [eachIndex, eachArguments] of INSTALL_MODES.entries()) {
|
|
22
|
+
const homeDirectory = join(sandbox, `home-${eachIndex}`);
|
|
23
|
+
execFileSync(process.execPath, [join(packaged, 'bin/install.mjs'), ...eachArguments], {
|
|
24
|
+
encoding: 'utf8',
|
|
25
|
+
env: { ...process.env, CLAUDE_CONFIG_DIR: undefined, LLM_SETTINGS_PROFILES_ROOT: undefined, HOME: homeDirectory, USERPROFILE: homeDirectory, CODEX_HOME: join(homeDirectory, '.codex'), GIT_CONFIG_GLOBAL: join(homeDirectory, '.gitconfig'), CDE_INSTALL_PSTACK: '0', CDE_INSTALL_DEEPSTACK: '0', CDE_INSTALL_USAGE_WRAPUP: '0', CDE_INSTALL_SUBAGENT_MODELS: '0', CDE_INSTALL_DEEPEN_FIRST_PROMPT: '0' },
|
|
26
|
+
});
|
|
27
|
+
const installed = join(homeDirectory, ...SKILL_PATH_SEGMENTS);
|
|
28
|
+
for (const eachFile of REQUIRED_FILES) assert.deepEqual(readFileSync(join(installed, eachFile)), readFileSync(join(packaged, ...SKILL_PATH_SEGMENTS, eachFile)));
|
|
29
|
+
assert.equal(realpathSync(join(homeDirectory, '.claude/skills/pr-visual')), realpathSync(installed));
|
|
30
|
+
const manifest = JSON.parse(readFileSync(join(homeDirectory, '.claude/.claude-dev-env-manifest.json'), 'utf8'));
|
|
31
|
+
assert.ok(manifest.skills.includes('pr-visual'));
|
|
32
|
+
const visual = join(sandbox, `visual-${eachIndex}.html`);
|
|
33
|
+
execFileSync(process.platform === 'win32' ? 'python' : 'python3', [join(installed, 'scripts/render.py'), join(installed, 'fixtures/integrated.scene.json'), '--out', visual]);
|
|
34
|
+
assert.ok(existsSync(visual));
|
|
35
|
+
assert.match(readFileSync(visual, 'utf8'), /Duration \(ms\)/);
|
|
36
|
+
}
|
|
37
|
+
} finally {
|
|
38
|
+
rmSync(sandbox, { recursive: true, force: true });
|
|
39
|
+
}
|
|
40
|
+
});
|
|
@@ -35,12 +35,11 @@ test('reinstall drops the retired style reminder and keeps one skill reminder ho
|
|
|
35
35
|
mergeHooksIntoSettings(settings, hooksConfig, 'C:/Users/example/.claude', 'python');
|
|
36
36
|
|
|
37
37
|
const userPromptHooks = settings.hooks.UserPromptSubmit.flatMap(eachGroup => eachGroup.hooks);
|
|
38
|
-
assert.equal(userPromptHooks.length,
|
|
38
|
+
assert.equal(userPromptHooks.length, 4);
|
|
39
39
|
assert.equal(userPromptHooks[0].command, userCommand);
|
|
40
40
|
assert.match(userPromptHooks[1].command, /session\/skill_loaded_reminder\.py$/);
|
|
41
|
-
assert.match(userPromptHooks[2].command, /
|
|
42
|
-
assert.match(userPromptHooks[3].command, /
|
|
43
|
-
assert.match(userPromptHooks[4].command, /blocking\/chat_reply_gate\.py$/);
|
|
41
|
+
assert.match(userPromptHooks[2].command, /lifecycle\/nested_project_hooks\.py$/);
|
|
42
|
+
assert.match(userPromptHooks[3].command, /blocking\/chat_reply_gate\.py$/);
|
|
44
43
|
assert.ok(userPromptHooks.every(eachHook => !eachHook.command.includes('style_reminder_prompt')));
|
|
45
44
|
|
|
46
45
|
const firstMerge = JSON.stringify(settings);
|
|
@@ -266,7 +266,6 @@ function resolveInstallerInvocation(homeDirectory) {
|
|
|
266
266
|
CDE_INSTALL_DEEPSTACK: '0',
|
|
267
267
|
CDE_INSTALL_USAGE_WRAPUP: '0',
|
|
268
268
|
CDE_INSTALL_SUBAGENT_MODELS: '0',
|
|
269
|
-
CDE_INSTALL_DEEPEN_FIRST_PROMPT: '0',
|
|
270
269
|
HOME: homeDirectory,
|
|
271
270
|
USERPROFILE: homeDirectory,
|
|
272
271
|
GIT_CONFIG_GLOBAL: join(homeDirectory, '.gitconfig'),
|
|
@@ -28,7 +28,6 @@ function installInScratchHome(homeDirectory, argumentsList = [], extraEnvironmen
|
|
|
28
28
|
CDE_INSTALL_DEEPSTACK: '0',
|
|
29
29
|
CDE_INSTALL_USAGE_WRAPUP: '0',
|
|
30
30
|
CDE_INSTALL_SUBAGENT_MODELS: '0',
|
|
31
|
-
CDE_INSTALL_DEEPEN_FIRST_PROMPT: '0',
|
|
32
31
|
HOME: homeDirectory,
|
|
33
32
|
USERPROFILE: homeDirectory,
|
|
34
33
|
CODEX_HOME: join(homeDirectory, '.codex'),
|
|
@@ -186,7 +185,6 @@ test('a successful pstack install omits the retired skill block and gains the qu
|
|
|
186
185
|
CDE_INSTALL_DEEPSTACK: '0',
|
|
187
186
|
CDE_INSTALL_USAGE_WRAPUP: '0',
|
|
188
187
|
CDE_INSTALL_SUBAGENT_MODELS: '0',
|
|
189
|
-
CDE_INSTALL_DEEPEN_FIRST_PROMPT: '0',
|
|
190
188
|
CDE_CODEX_EXECUTABLE: commandPath,
|
|
191
189
|
CDE_CLAUDE_EXECUTABLE: commandPath,
|
|
192
190
|
});
|
|
@@ -216,7 +214,6 @@ test('enabling pstack after a pstack-off install preserves question guidance', (
|
|
|
216
214
|
CDE_INSTALL_DEEPSTACK: '0',
|
|
217
215
|
CDE_INSTALL_USAGE_WRAPUP: '0',
|
|
218
216
|
CDE_INSTALL_SUBAGENT_MODELS: '0',
|
|
219
|
-
CDE_INSTALL_DEEPEN_FIRST_PROMPT: '0',
|
|
220
217
|
CDE_CODEX_EXECUTABLE: commandPath,
|
|
221
218
|
CDE_CLAUDE_EXECUTABLE: commandPath,
|
|
222
219
|
});
|
|
@@ -247,7 +244,6 @@ test('a failed pstack transition restores question-only guidance and removes its
|
|
|
247
244
|
CDE_INSTALL_DEEPSTACK: '0',
|
|
248
245
|
CDE_INSTALL_USAGE_WRAPUP: '0',
|
|
249
246
|
CDE_INSTALL_SUBAGENT_MODELS: '0',
|
|
250
|
-
CDE_INSTALL_DEEPEN_FIRST_PROMPT: '0',
|
|
251
247
|
CDE_CODEX_EXECUTABLE: commandPath,
|
|
252
248
|
CDE_CLAUDE_EXECUTABLE: commandPath,
|
|
253
249
|
CLAUDE_DEV_ENV_INSTALL_FAULT: 'after_file_staging',
|
|
@@ -70,7 +70,6 @@ function runInstallerInSandbox(sandboxHome, installerArguments = []) {
|
|
|
70
70
|
CDE_INSTALL_DEEPSTACK: '0',
|
|
71
71
|
CDE_INSTALL_USAGE_WRAPUP: '0',
|
|
72
72
|
CDE_INSTALL_SUBAGENT_MODELS: '0',
|
|
73
|
-
CDE_INSTALL_DEEPEN_FIRST_PROMPT: '0',
|
|
74
73
|
HOME: sandboxHome,
|
|
75
74
|
USERPROFILE: sandboxHome,
|
|
76
75
|
GIT_CONFIG_GLOBAL: gitConfigGlobal,
|
package/bin/install.test.mjs
CHANGED
|
@@ -57,6 +57,7 @@ import {
|
|
|
57
57
|
} from './install.mjs';
|
|
58
58
|
import { EVER_SHIPPED_SKILL_NAMES } from './ever-shipped-skills.mjs';
|
|
59
59
|
import {
|
|
60
|
+
DEEPEN_GUIDANCE_BLOCK_END,
|
|
60
61
|
DEEPEN_GUIDANCE_BLOCK_START,
|
|
61
62
|
PACKAGE_GUIDANCE_BLOCK_END,
|
|
62
63
|
PACKAGE_GUIDANCE_BLOCK_START,
|
|
@@ -257,9 +258,9 @@ test('CORE_SKILLS ships ELI5 as the leaf presentation skill', () => {
|
|
|
257
258
|
assert.equal(INSTALL_GROUPS.core.skills.includes('eli5'), true);
|
|
258
259
|
});
|
|
259
260
|
|
|
260
|
-
test('CORE_SKILLS
|
|
261
|
-
assert.
|
|
262
|
-
assert.equal(INSTALL_GROUPS.core.skills.includes('deepen'),
|
|
261
|
+
test('CORE_SKILLS leaves out the retired deepen skill', () => {
|
|
262
|
+
assert.equal(CORE_SKILLS.includes('deepen'), false);
|
|
263
|
+
assert.equal(INSTALL_GROUPS.core.skills.includes('deepen'), false);
|
|
263
264
|
});
|
|
264
265
|
|
|
265
266
|
test('CORE_SKILLS ships efficient-review so a core install includes the skill', () => {
|
|
@@ -1243,6 +1244,7 @@ test('retired hook registrations stay managed so reinstall removes them', () =>
|
|
|
1243
1244
|
'blocking/artifact_look_gate.py',
|
|
1244
1245
|
'blocking/session_title_stop_gate.py',
|
|
1245
1246
|
'session/issue_tracker_session_starter.py',
|
|
1247
|
+
'session/deepen_first_prompt.py',
|
|
1246
1248
|
]);
|
|
1247
1249
|
const shippedHooks = JSON.parse(
|
|
1248
1250
|
readFileSync(new URL('../hooks/hooks.json', import.meta.url), 'utf8')
|
|
@@ -3048,7 +3050,6 @@ test('a failed update preserves an existing skills lookup pointer', () => {
|
|
|
3048
3050
|
CDE_INSTALL_DEEPSTACK: '0',
|
|
3049
3051
|
CDE_INSTALL_USAGE_WRAPUP: '0',
|
|
3050
3052
|
CDE_INSTALL_SUBAGENT_MODELS: '0',
|
|
3051
|
-
CDE_INSTALL_DEEPEN_FIRST_PROMPT: '0',
|
|
3052
3053
|
};
|
|
3053
3054
|
const argumentsForCoreInstall = [PSTACK_TEST_INSTALLER_PATH, '--only', 'core'];
|
|
3054
3055
|
const firstRun = spawnSync(process.execPath, argumentsForCoreInstall, {
|
|
@@ -3103,7 +3104,6 @@ function runPstackInstaller(homeDirectory, extraArguments, environmentOverrides
|
|
|
3103
3104
|
CDE_INSTALL_DEEPSTACK: '0',
|
|
3104
3105
|
CDE_INSTALL_USAGE_WRAPUP: '0',
|
|
3105
3106
|
CDE_INSTALL_SUBAGENT_MODELS: '0',
|
|
3106
|
-
CDE_INSTALL_DEEPEN_FIRST_PROMPT: '0',
|
|
3107
3107
|
...environmentOverrides,
|
|
3108
3108
|
},
|
|
3109
3109
|
});
|
|
@@ -3213,10 +3213,7 @@ test('a full install adds DeepStack for Claude, Codex, and Cursor', t => {
|
|
|
3213
3213
|
hooks.hooks.sessionStart.filter(entry => entry.command.includes('session-start.sh')).length,
|
|
3214
3214
|
1,
|
|
3215
3215
|
);
|
|
3216
|
-
assert.equal(
|
|
3217
|
-
hooks.hooks.beforeSubmitPrompt.filter(entry => entry.command.includes('deepen_first_prompt.py')).length,
|
|
3218
|
-
1,
|
|
3219
|
-
);
|
|
3216
|
+
assert.equal(JSON.stringify(hooks).includes('deepen_first_prompt.py'), false, 'no retired first-task hook');
|
|
3220
3217
|
assert.equal(JSON.stringify(hooks).includes('"hooks":[{'), false, 'no Claude-style group entries');
|
|
3221
3218
|
const sheet = readFileSync(join(sandbox.homeDirectory, '.codex', 'deepstack-models.md'), 'utf8');
|
|
3222
3219
|
assert.match(sheet, /^session hook: on$/m);
|
|
@@ -3228,7 +3225,8 @@ test('a repeat install preserves Codex pstack configuration', t => {
|
|
|
3228
3225
|
runPstackInstaller(sandbox.homeDirectory, [], sandbox.environment);
|
|
3229
3226
|
writeFileSync(
|
|
3230
3227
|
join(codexHome, 'AGENTS.md'),
|
|
3231
|
-
`${SKILL_LOAD_BLOCK_START}\n${STALE_SKILL_LOAD_SENTENCE}\n${SKILL_LOAD_BLOCK_END}\nCustom Codex guidance\n
|
|
3228
|
+
`${SKILL_LOAD_BLOCK_START}\n${STALE_SKILL_LOAD_SENTENCE}\n${SKILL_LOAD_BLOCK_END}\nCustom Codex guidance\n`
|
|
3229
|
+
+ `${DEEPEN_GUIDANCE_BLOCK_START}\nDeepen the first interactive user task.\n${DEEPEN_GUIDANCE_BLOCK_END}\n`,
|
|
3232
3230
|
);
|
|
3233
3231
|
writeFileSync(join(codexHome, 'pstack-models.md'), 'session hook: off\n');
|
|
3234
3232
|
|
|
@@ -3238,7 +3236,7 @@ test('a repeat install preserves Codex pstack configuration', t => {
|
|
|
3238
3236
|
assert.ok(codexGuidance.startsWith('Custom Codex guidance\n'));
|
|
3239
3237
|
assertOmitsSkillLoadSentence(codexGuidance);
|
|
3240
3238
|
assert.ok(codexGuidance.includes(QUESTION_PRESENTATION_BLOCK_START));
|
|
3241
|
-
assert.
|
|
3239
|
+
assert.equal(codexGuidance.includes(DEEPEN_GUIDANCE_BLOCK_START), false);
|
|
3242
3240
|
assert.equal(readFileSync(join(codexHome, 'pstack-models.md'), 'utf8'), 'session hook: off\n');
|
|
3243
3241
|
});
|
|
3244
3242
|
|
|
@@ -3322,7 +3320,7 @@ test('a fresh full install writes the package guidance into Codex AGENTS.md', t
|
|
|
3322
3320
|
assertHoldsPackageGuidance(codexGuidance);
|
|
3323
3321
|
assertOmitsSkillLoadSentence(codexGuidance);
|
|
3324
3322
|
assert.ok(codexGuidance.includes(QUESTION_PRESENTATION_BLOCK_START));
|
|
3325
|
-
assert.
|
|
3323
|
+
assert.equal(codexGuidance.includes(DEEPEN_GUIDANCE_BLOCK_START), false);
|
|
3326
3324
|
});
|
|
3327
3325
|
|
|
3328
3326
|
test('--no-pstack writes package and question guidance without the skill-load block', t => {
|
|
@@ -3405,7 +3403,7 @@ test('a pstack install after a --no-pstack install seeds the Codex model sheet',
|
|
|
3405
3403
|
assertOmitsSkillLoadSentence(codexGuidance);
|
|
3406
3404
|
assertHoldsPackageGuidance(codexGuidance);
|
|
3407
3405
|
assert.ok(codexGuidance.includes(QUESTION_PRESENTATION_BLOCK_START));
|
|
3408
|
-
assert.
|
|
3406
|
+
assert.equal(codexGuidance.includes(DEEPEN_GUIDANCE_BLOCK_START), false);
|
|
3409
3407
|
});
|
|
3410
3408
|
|
|
3411
3409
|
const USAGE_WRAPUP_CLAUDE_COMMANDS = Object.freeze([
|
|
@@ -3483,18 +3481,16 @@ test('the help output names the usage-wrapup opt-out beside the pstack one', ()
|
|
|
3483
3481
|
|
|
3484
3482
|
assert.equal(helpRun.status, 0, helpRun.stderr);
|
|
3485
3483
|
assert.match(helpRun.stdout, /--no-pstack .*\n.*--no-deepstack .*CDE_INSTALL_DEEPSTACK=0.*\n.*--no-usage-wrapup .*CDE_INSTALL_USAGE_WRAPUP=0/);
|
|
3486
|
-
assert.
|
|
3484
|
+
assert.doesNotMatch(helpRun.stdout, /--no-deepen-first-prompt/);
|
|
3487
3485
|
});
|
|
3488
3486
|
|
|
3489
3487
|
const REPOSITORY_PLUGIN_CLAUDE_COMMANDS = Object.freeze([
|
|
3490
3488
|
'claude plugin uninstall usage-wrapup@claude-dev-env',
|
|
3491
3489
|
'claude plugin uninstall subagent-models@claude-dev-env',
|
|
3492
|
-
'claude plugin uninstall deepen-first-prompt@claude-dev-env',
|
|
3493
3490
|
'claude plugin marketplace remove claude-dev-env',
|
|
3494
|
-
'claude plugin marketplace add jl-cmd-projects/claude-dev-env --sparse .claude-plugin packages/usage-wrapup packages/subagent-models
|
|
3491
|
+
'claude plugin marketplace add jl-cmd-projects/claude-dev-env --sparse .claude-plugin packages/usage-wrapup packages/subagent-models',
|
|
3495
3492
|
'claude plugin install usage-wrapup@claude-dev-env',
|
|
3496
3493
|
'claude plugin install subagent-models@claude-dev-env',
|
|
3497
|
-
'claude plugin install deepen-first-prompt@claude-dev-env',
|
|
3498
3494
|
]);
|
|
3499
3495
|
|
|
3500
3496
|
test('should install subagent-models beside usage-wrapup with one marketplace add on a full install', t => {
|
|
@@ -3504,13 +3500,12 @@ test('should install subagent-models beside usage-wrapup with one marketplace ad
|
|
|
3504
3500
|
...sandbox.environment,
|
|
3505
3501
|
CDE_INSTALL_USAGE_WRAPUP: '1',
|
|
3506
3502
|
CDE_INSTALL_SUBAGENT_MODELS: '1',
|
|
3507
|
-
CDE_INSTALL_DEEPEN_FIRST_PROMPT: '1',
|
|
3508
3503
|
});
|
|
3509
3504
|
|
|
3510
3505
|
assert.deepEqual(usageWrapupCommands(sandbox.recordedCommands()), REPOSITORY_PLUGIN_CLAUDE_COMMANDS);
|
|
3511
3506
|
assert.match(installerOutput, /Usage-wrapup \(claude\): installed/);
|
|
3512
3507
|
assert.match(installerOutput, /Subagent-models \(claude\): installed/);
|
|
3513
|
-
assert.
|
|
3508
|
+
assert.doesNotMatch(installerOutput, /Deepen-first-prompt \(/);
|
|
3514
3509
|
});
|
|
3515
3510
|
|
|
3516
3511
|
for (const [caseName, extraArguments, environmentOverrides] of [
|
|
@@ -3534,27 +3529,6 @@ for (const [caseName, extraArguments, environmentOverrides] of [
|
|
|
3534
3529
|
});
|
|
3535
3530
|
}
|
|
3536
3531
|
|
|
3537
|
-
for (const [caseName, extraArguments, environmentOverrides] of [
|
|
3538
|
-
['--no-deepen-first-prompt', ['--no-deepen-first-prompt'], { CDE_INSTALL_DEEPEN_FIRST_PROMPT: '1' }],
|
|
3539
|
-
['CDE_INSTALL_DEEPEN_FIRST_PROMPT=0', [], { CDE_INSTALL_DEEPEN_FIRST_PROMPT: '0' }],
|
|
3540
|
-
['an --only run', ['--only', 'core'], { CDE_INSTALL_DEEPEN_FIRST_PROMPT: '1' }],
|
|
3541
|
-
]) {
|
|
3542
|
-
test(`should skip deepen-first-prompt on ${caseName}`, t => {
|
|
3543
|
-
const sandbox = pstackPluginSandbox(t);
|
|
3544
|
-
|
|
3545
|
-
const installerOutput = runPstackInstaller(sandbox.homeDirectory, extraArguments, {
|
|
3546
|
-
...sandbox.environment,
|
|
3547
|
-
...environmentOverrides,
|
|
3548
|
-
});
|
|
3549
|
-
|
|
3550
|
-
assert.equal(
|
|
3551
|
-
sandbox.recordedCommands().some(eachCommand => eachCommand.includes('deepen-first-prompt')),
|
|
3552
|
-
false,
|
|
3553
|
-
);
|
|
3554
|
-
assert.doesNotMatch(installerOutput, /Deepen-first-prompt \(/);
|
|
3555
|
-
});
|
|
3556
|
-
}
|
|
3557
|
-
|
|
3558
3532
|
test('should name the subagent-models opt-out in the help output', () => {
|
|
3559
3533
|
const helpRun = spawnSync(process.execPath, [PSTACK_TEST_INSTALLER_PATH, '--help'], {
|
|
3560
3534
|
encoding: 'utf8',
|
|
@@ -3649,7 +3623,6 @@ function installWithGlobalPrefix(context, globalPrefix) {
|
|
|
3649
3623
|
CDE_INSTALL_DEEPSTACK: '0',
|
|
3650
3624
|
CDE_INSTALL_USAGE_WRAPUP: '0',
|
|
3651
3625
|
CDE_INSTALL_SUBAGENT_MODELS: '0',
|
|
3652
|
-
CDE_INSTALL_DEEPEN_FIRST_PROMPT: '0',
|
|
3653
3626
|
HOME: homeDirectory,
|
|
3654
3627
|
USERPROFILE: homeDirectory,
|
|
3655
3628
|
APPDATA: join(homeDirectory, 'AppData', 'Roaming'),
|
|
@@ -3706,7 +3679,6 @@ test('install adds the open-tasks Stop gate to every profile with its own settin
|
|
|
3706
3679
|
CDE_INSTALL_DEEPSTACK: '0',
|
|
3707
3680
|
CDE_INSTALL_USAGE_WRAPUP: '0',
|
|
3708
3681
|
CDE_INSTALL_SUBAGENT_MODELS: '0',
|
|
3709
|
-
CDE_INSTALL_DEEPEN_FIRST_PROMPT: '0',
|
|
3710
3682
|
HOME: homeDirectory,
|
|
3711
3683
|
USERPROFILE: homeDirectory,
|
|
3712
3684
|
CODEX_HOME: join(homeDirectory, '.codex'),
|
|
@@ -3749,7 +3721,6 @@ test('a profile settings.json that is not valid JSON is left alone and named und
|
|
|
3749
3721
|
CDE_INSTALL_DEEPSTACK: '0',
|
|
3750
3722
|
CDE_INSTALL_USAGE_WRAPUP: '0',
|
|
3751
3723
|
CDE_INSTALL_SUBAGENT_MODELS: '0',
|
|
3752
|
-
CDE_INSTALL_DEEPEN_FIRST_PROMPT: '0',
|
|
3753
3724
|
HOME: homeDirectory,
|
|
3754
3725
|
USERPROFILE: homeDirectory,
|
|
3755
3726
|
CODEX_HOME: join(homeDirectory, '.codex'),
|
|
@@ -3795,7 +3766,6 @@ test('uninstall stops before changing anything while a profile settings.json tha
|
|
|
3795
3766
|
CDE_INSTALL_DEEPSTACK: '0',
|
|
3796
3767
|
CDE_INSTALL_USAGE_WRAPUP: '0',
|
|
3797
3768
|
CDE_INSTALL_SUBAGENT_MODELS: '0',
|
|
3798
|
-
CDE_INSTALL_DEEPEN_FIRST_PROMPT: '0',
|
|
3799
3769
|
HOME: homeDirectory,
|
|
3800
3770
|
USERPROFILE: homeDirectory,
|
|
3801
3771
|
CODEX_HOME: join(homeDirectory, '.codex'),
|