@cleocode/adapters 2026.10.4 → 2026.10.5-rc.ci.672
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/dist/index.js +1608 -1380
- package/dist/index.js.map +3 -3
- package/dist/providers/claude-code/adapter.d.ts.map +1 -1
- package/dist/providers/claude-code/hooks.d.ts +43 -17
- package/dist/providers/claude-code/hooks.d.ts.map +1 -1
- package/dist/providers/claude-code/install.d.ts +42 -14
- package/dist/providers/claude-code/install.d.ts.map +1 -1
- package/dist/providers/claude-code/paths.d.ts +77 -0
- package/dist/providers/claude-code/paths.d.ts.map +1 -1
- package/dist/providers/codex/install.d.ts.map +1 -1
- package/dist/providers/cursor/install.d.ts.map +1 -1
- package/dist/providers/gemini-cli/install.d.ts.map +1 -1
- package/dist/providers/kimi/install.d.ts.map +1 -1
- package/dist/providers/openai-sdk/install.d.ts.map +1 -1
- package/dist/providers/opencode/install.d.ts.map +1 -1
- package/dist/providers/pi/install.d.ts.map +1 -1
- package/dist/providers/shared/heavy-command-hook-install.d.ts.map +1 -1
- package/dist/providers/shared/project-instruction-file.d.ts +30 -0
- package/dist/providers/shared/project-instruction-file.d.ts.map +1 -0
- package/package.json +5 -5
- package/src/__tests__/claude-code-adapter.test.ts +11 -6
- package/src/providers/claude-code/__tests__/hooks-install.test.ts +20 -17
- package/src/providers/claude-code/__tests__/no-user-global-write.test.ts +293 -0
- package/src/providers/claude-code/adapter.ts +3 -1
- package/src/providers/claude-code/hooks.ts +138 -51
- package/src/providers/claude-code/install.ts +123 -52
- package/src/providers/claude-code/paths.ts +159 -1
- package/src/providers/codex/install.ts +4 -2
- package/src/providers/cursor/install.ts +26 -12
- package/src/providers/gemini-cli/install.ts +4 -2
- package/src/providers/kimi/install.ts +4 -2
- package/src/providers/openai-sdk/install.ts +5 -2
- package/src/providers/opencode/install.ts +4 -2
- package/src/providers/pi/install.ts +4 -2
- package/src/providers/shared/__tests__/heavy-command-hook-install.test.ts +20 -0
- package/src/providers/shared/heavy-command-hook-install.ts +6 -1
- package/src/providers/shared/project-instruction-file.ts +47 -0
|
@@ -14,6 +14,7 @@ import { join } from 'node:path';
|
|
|
14
14
|
import { ensureProviderInstructionFile } from '@cleocode/caamp';
|
|
15
15
|
import type { AdapterInstallProvider, InstallOptions, InstallResult } from '@cleocode/contracts';
|
|
16
16
|
import { getCleoTemplatesTildePath } from '../shared/paths.js';
|
|
17
|
+
import { ensureProjectInstructionFile } from '../shared/project-instruction-file.js';
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
* Install provider for Kimi.
|
|
@@ -58,15 +59,16 @@ export class KimiInstallProvider implements AdapterInstallProvider {
|
|
|
58
59
|
const installedAt = new Date().toISOString();
|
|
59
60
|
const details: Record<string, unknown> = {};
|
|
60
61
|
|
|
61
|
-
const result = await
|
|
62
|
+
const result = await ensureProjectInstructionFile('kimi', projectDir, {
|
|
62
63
|
references: [`@${getCleoTemplatesTildePath()}/CLEO-INJECTION.md`, '@.cleo/memory-bridge.md'],
|
|
63
64
|
scope: 'project',
|
|
64
65
|
});
|
|
65
66
|
|
|
66
|
-
const instructionFileUpdated = result.action !== 'intact';
|
|
67
|
+
const instructionFileUpdated = result !== null && result.action !== 'intact';
|
|
67
68
|
if (instructionFileUpdated) {
|
|
68
69
|
details.instructionFile = result.filePath;
|
|
69
70
|
}
|
|
71
|
+
if (result === null) details.instructionFile = 'skipped'; // T13227: $HOME project
|
|
70
72
|
|
|
71
73
|
return {
|
|
72
74
|
success: true,
|
|
@@ -14,6 +14,7 @@ import { join } from 'node:path';
|
|
|
14
14
|
import { ensureProviderInstructionFile } from '@cleocode/caamp';
|
|
15
15
|
import type { AdapterInstallProvider, InstallOptions, InstallResult } from '@cleocode/contracts';
|
|
16
16
|
import { getCleoTemplatesTildePath } from '../shared/paths.js';
|
|
17
|
+
import { ensureProjectInstructionFile } from '../shared/project-instruction-file.js';
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
* Install provider for the OpenAI SDK adapter (Vercel AI SDK).
|
|
@@ -40,15 +41,17 @@ export class OpenAiSdkInstallProvider implements AdapterInstallProvider {
|
|
|
40
41
|
const details: Record<string, unknown> = {};
|
|
41
42
|
|
|
42
43
|
// Step 1: Ensure AGENTS.md has @-references via CAAMP registry
|
|
43
|
-
const instructionResult = await
|
|
44
|
+
const instructionResult = await ensureProjectInstructionFile('openai-sdk', projectDir, {
|
|
44
45
|
references: [`@${getCleoTemplatesTildePath()}/CLEO-INJECTION.md`, '@.cleo/memory-bridge.md'],
|
|
45
46
|
scope: 'project',
|
|
46
47
|
});
|
|
47
48
|
|
|
48
|
-
const instructionFileUpdated =
|
|
49
|
+
const instructionFileUpdated =
|
|
50
|
+
instructionResult !== null && instructionResult.action !== 'intact';
|
|
49
51
|
if (instructionFileUpdated) {
|
|
50
52
|
details.instructionFile = instructionResult.filePath;
|
|
51
53
|
}
|
|
54
|
+
if (instructionResult === null) details.instructionFile = 'skipped'; // T13227: $HOME project
|
|
52
55
|
|
|
53
56
|
// Step 2: Create .openai config directory stub
|
|
54
57
|
const configCreated = this.ensureConfigDir(projectDir);
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
installProviderHookTemplates,
|
|
26
26
|
} from '../shared/hook-template-installer.js';
|
|
27
27
|
import { getCleoTemplatesTildePath } from '../shared/paths.js';
|
|
28
|
+
import { ensureProjectInstructionFile } from '../shared/project-instruction-file.js';
|
|
28
29
|
|
|
29
30
|
/**
|
|
30
31
|
* Install provider for OpenCode.
|
|
@@ -57,15 +58,16 @@ export class OpenCodeInstallProvider implements AdapterInstallProvider {
|
|
|
57
58
|
const details: Record<string, unknown> = {};
|
|
58
59
|
|
|
59
60
|
// Step 1: Ensure AGENTS.md has @-references via CAAMP canonical API.
|
|
60
|
-
const instructResult = await
|
|
61
|
+
const instructResult = await ensureProjectInstructionFile('opencode', projectDir, {
|
|
61
62
|
scope: 'project',
|
|
62
63
|
references: [`@${getCleoTemplatesTildePath()}/CLEO-INJECTION.md`, '@.cleo/memory-bridge.md'],
|
|
63
64
|
});
|
|
64
65
|
|
|
65
|
-
const instructionFileUpdated = instructResult.action !== 'intact';
|
|
66
|
+
const instructionFileUpdated = instructResult !== null && instructResult.action !== 'intact';
|
|
66
67
|
if (instructionFileUpdated) {
|
|
67
68
|
details.instructionFile = join(projectDir, instructResult.instructFile);
|
|
68
69
|
}
|
|
70
|
+
if (instructResult === null) details.instructionFile = 'skipped'; // T13227: $HOME project
|
|
69
71
|
|
|
70
72
|
// Step 2 (T1013): Install PreCompact hook templates + generate the JS
|
|
71
73
|
// plugin wrapper that spawns the bash shim on
|
|
@@ -19,6 +19,7 @@ import { join } from 'node:path';
|
|
|
19
19
|
import { ensureProviderInstructionFile } from '@cleocode/caamp';
|
|
20
20
|
import type { AdapterInstallProvider, InstallOptions, InstallResult } from '@cleocode/contracts';
|
|
21
21
|
import { getCleoTemplatesTildePath } from '../shared/paths.js';
|
|
22
|
+
import { ensureProjectInstructionFile } from '../shared/project-instruction-file.js';
|
|
22
23
|
|
|
23
24
|
/**
|
|
24
25
|
* CLEO @-references injected into Pi instruction files.
|
|
@@ -55,15 +56,16 @@ export class PiInstallProvider implements AdapterInstallProvider {
|
|
|
55
56
|
const details: Record<string, unknown> = {};
|
|
56
57
|
|
|
57
58
|
// Step 1: Ensure project AGENTS.md has @-references via CAAMP canonical API.
|
|
58
|
-
const projectResult = await
|
|
59
|
+
const projectResult = await ensureProjectInstructionFile('pi', projectDir, {
|
|
59
60
|
scope: 'project',
|
|
60
61
|
references: getCleoReferences(),
|
|
61
62
|
});
|
|
62
63
|
|
|
63
|
-
const projectUpdated = projectResult.action !== 'intact';
|
|
64
|
+
const projectUpdated = projectResult !== null && projectResult.action !== 'intact';
|
|
64
65
|
if (projectUpdated) {
|
|
65
66
|
details.instructionFile = join(projectDir, projectResult.instructFile);
|
|
66
67
|
}
|
|
68
|
+
if (projectResult === null) details.instructionFile = 'skipped'; // T13227: $HOME project
|
|
67
69
|
|
|
68
70
|
// Step 2: Ensure global AGENTS.md has @-references (best-effort).
|
|
69
71
|
// CAAMP resolves pathGlobal from the registry ($HOME/.pi/agent).
|
|
@@ -43,6 +43,7 @@ import { pathToFileURL } from 'node:url';
|
|
|
43
43
|
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
44
44
|
import { ClaudeCodeInstallProvider } from '../../claude-code/install.js';
|
|
45
45
|
import { CodexInstallProvider } from '../../codex/install.js';
|
|
46
|
+
import { CursorInstallProvider } from '../../cursor/install.js';
|
|
46
47
|
import { OpenCodeInstallProvider } from '../../opencode/install.js';
|
|
47
48
|
import {
|
|
48
49
|
clearOlderCleoMarkers,
|
|
@@ -591,6 +592,25 @@ describe('installer wiring', () => {
|
|
|
591
592
|
false,
|
|
592
593
|
);
|
|
593
594
|
expect(isUserHomeDir(join(home, 'project'))).toBe(false);
|
|
595
|
+
// T13227: no instruction file at $HOME either; providers load ~/CLAUDE.md
|
|
596
|
+
// and ~/AGENTS.md into every session under it.
|
|
597
|
+
expect(results.map((r) => r.details?.instructionFile)).toEqual([
|
|
598
|
+
'skipped',
|
|
599
|
+
'skipped',
|
|
600
|
+
'skipped',
|
|
601
|
+
]);
|
|
602
|
+
expect(existsSync(join(home, 'CLAUDE.md'))).toBe(false);
|
|
603
|
+
expect(existsSync(join(home, 'AGENTS.md'))).toBe(false);
|
|
604
|
+
});
|
|
605
|
+
|
|
606
|
+
it("T13257: Cursor at $HOME skips its hooks (~/.cursor/hooks.json is Cursor's user-global hooks) and rule files", async () => {
|
|
607
|
+
const home = process.env.HOME as string;
|
|
608
|
+
const r = await new CursorInstallProvider().install({ projectDir: home });
|
|
609
|
+
expect(r.details?.hookTemplates).toBe('skipped');
|
|
610
|
+
expect(r.details?.instructionFiles).toBe('skipped');
|
|
611
|
+
expect(existsSync(join(home, '.cursor', 'hooks.json'))).toBe(false);
|
|
612
|
+
expect(existsSync(join(home, '.cursor', 'hooks'))).toBe(false);
|
|
613
|
+
expect(existsSync(join(home, '.cursorrules'))).toBe(false);
|
|
594
614
|
});
|
|
595
615
|
|
|
596
616
|
it('Codex and opencode install only when a mode is passed', async () => {
|
|
@@ -101,9 +101,14 @@ export const HEAVY_TOOL_WORDS: readonly string[] = Object.freeze([
|
|
|
101
101
|
*/
|
|
102
102
|
export type HeavyHookSyncResult = 'installed' | 'updated' | 'removed' | 'unchanged' | 'skipped';
|
|
103
103
|
|
|
104
|
+
/**
|
|
105
|
+
* `path` through the native realpath, which returns the case stored on disk,
|
|
106
|
+
* so a differently cased spelling of the home directory on a case-insensitive
|
|
107
|
+
* volume still matches it (T13227 review).
|
|
108
|
+
*/
|
|
104
109
|
function canonical(path: string): string {
|
|
105
110
|
try {
|
|
106
|
-
return realpathSync(path);
|
|
111
|
+
return realpathSync.native(path);
|
|
107
112
|
} catch {
|
|
108
113
|
return resolve(path);
|
|
109
114
|
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A provider's PROJECT instruction file, skipped (not failed) at a project
|
|
3
|
+
* rooted at the home directory (T13227).
|
|
4
|
+
*
|
|
5
|
+
* Providers load CLAUDE.md / AGENTS.md / GEMINI.md from the working directory
|
|
6
|
+
* up through every ancestor, so a file at `$HOME` reaches every session under
|
|
7
|
+
* it: CAAMP refuses it with `HomeInstructionFileError`. An adapter's install
|
|
8
|
+
* reports that step as `skipped` and carries on with the rest, the way the
|
|
9
|
+
* heavy-command hook already skips the home directory (T12983).
|
|
10
|
+
*
|
|
11
|
+
* @task T13227
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
type EnsureProviderInstructionFileOptions,
|
|
16
|
+
type EnsureProviderInstructionFileResult,
|
|
17
|
+
ensureProviderInstructionFile,
|
|
18
|
+
HomeInstructionFileError,
|
|
19
|
+
} from '@cleocode/caamp';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Ensure a provider's project instruction file, or `null` when the project is
|
|
23
|
+
* the home directory and the file was refused (nothing written).
|
|
24
|
+
*
|
|
25
|
+
* @param providerId - CAAMP provider id.
|
|
26
|
+
* @param projectDir - the project root.
|
|
27
|
+
* @param options - references, content, scope.
|
|
28
|
+
* @returns the CAAMP result, or `null` when skipped at `$HOME`.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* const r = await ensureProjectInstructionFile('codex', projectDir, { scope: 'project' });
|
|
33
|
+
* if (r === null) details.instructionFile = 'skipped';
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export async function ensureProjectInstructionFile(
|
|
37
|
+
providerId: string,
|
|
38
|
+
projectDir: string,
|
|
39
|
+
options: EnsureProviderInstructionFileOptions,
|
|
40
|
+
): Promise<EnsureProviderInstructionFileResult | null> {
|
|
41
|
+
try {
|
|
42
|
+
return await ensureProviderInstructionFile(providerId, projectDir, options);
|
|
43
|
+
} catch (err) {
|
|
44
|
+
if (err instanceof HomeInstructionFileError) return null;
|
|
45
|
+
throw err;
|
|
46
|
+
}
|
|
47
|
+
}
|