@haystackeditor/cli 0.7.2 → 0.8.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/README.md +59 -12
- package/dist/assets/hooks/agent-context/detect.ts +136 -0
- package/dist/assets/hooks/agent-context/format.ts +99 -0
- package/dist/assets/hooks/agent-context/index.ts +39 -0
- package/dist/assets/hooks/agent-context/parsers/claude.ts +253 -0
- package/dist/assets/hooks/agent-context/parsers/gemini.ts +155 -0
- package/dist/assets/hooks/agent-context/parsers/opencode.ts +174 -0
- package/dist/assets/hooks/agent-context/tsconfig.json +13 -0
- package/dist/assets/hooks/agent-context/types.ts +58 -0
- package/dist/assets/hooks/llm-rules-template.md +35 -0
- package/dist/assets/hooks/package.json +11 -0
- package/dist/assets/hooks/scripts/commit-msg.sh +4 -0
- package/dist/assets/hooks/scripts/post-commit.sh +4 -0
- package/dist/assets/hooks/scripts/pre-commit.sh +92 -0
- package/dist/assets/hooks/scripts/pre-push.sh +5 -0
- package/dist/assets/hooks/scripts/prepare-commit-msg.sh +3 -0
- package/dist/assets/hooks/truncation-checker/ast-analyzer.ts +528 -0
- package/dist/assets/hooks/truncation-checker/index.ts +595 -0
- package/dist/assets/hooks/truncation-checker/tsconfig.json +13 -0
- package/dist/commands/config.d.ts +14 -0
- package/dist/commands/config.js +89 -0
- package/dist/commands/hooks.d.ts +17 -0
- package/dist/commands/hooks.js +269 -0
- package/dist/commands/init.d.ts +1 -1
- package/dist/commands/init.js +20 -239
- package/dist/commands/secrets.d.ts +15 -0
- package/dist/commands/secrets.js +83 -0
- package/dist/commands/skills.d.ts +8 -0
- package/dist/commands/skills.js +215 -0
- package/dist/index.js +107 -7
- package/dist/types.d.ts +32 -8
- package/dist/utils/hooks.d.ts +26 -0
- package/dist/utils/hooks.js +226 -0
- package/dist/utils/skill.d.ts +1 -1
- package/dist/utils/skill.js +481 -13
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -15,8 +15,10 @@ import { Command } from 'commander';
|
|
|
15
15
|
import { statusCommand } from './commands/status.js';
|
|
16
16
|
import { initCommand } from './commands/init.js';
|
|
17
17
|
import { loginCommand, logoutCommand } from './commands/login.js';
|
|
18
|
-
import { listSecrets, setSecret, deleteSecret } from './commands/secrets.js';
|
|
19
|
-
import { handleSandbox } from './commands/config.js';
|
|
18
|
+
import { listSecrets, setSecret, getSecret, getSecretsForRepo, deleteSecret } from './commands/secrets.js';
|
|
19
|
+
import { handleSandbox, handleAgenticTool } from './commands/config.js';
|
|
20
|
+
import { installSkills, listSkills } from './commands/skills.js';
|
|
21
|
+
import { hooksInstall, hooksStatus, hooksUpdate } from './commands/hooks.js';
|
|
20
22
|
const program = new Command();
|
|
21
23
|
program
|
|
22
24
|
.name('haystack')
|
|
@@ -25,14 +27,15 @@ program
|
|
|
25
27
|
program
|
|
26
28
|
.command('init')
|
|
27
29
|
.description('Create .haystack.json configuration')
|
|
28
|
-
.option('-
|
|
30
|
+
.option('-f, --force', 'Overwrite existing .haystack.json')
|
|
29
31
|
.addHelpText('after', `
|
|
30
|
-
This creates a .haystack.json file
|
|
31
|
-
•
|
|
32
|
-
•
|
|
32
|
+
This creates a .haystack.json file with auto-detected settings:
|
|
33
|
+
• Dev server command and port
|
|
34
|
+
• Services (for monorepos)
|
|
33
35
|
• Auth bypass for sandbox environments
|
|
34
36
|
|
|
35
|
-
|
|
37
|
+
After running, use /setup-haystack in Claude Code (or give your AI agent
|
|
38
|
+
.agents/skills/setup-haystack.md) to add verification flows.
|
|
36
39
|
`)
|
|
37
40
|
.action(initCommand);
|
|
38
41
|
program
|
|
@@ -63,6 +66,20 @@ secrets
|
|
|
63
66
|
.action((key, value, options) => {
|
|
64
67
|
setSecret(key, value, options);
|
|
65
68
|
});
|
|
69
|
+
secrets
|
|
70
|
+
.command('get <key>')
|
|
71
|
+
.description('Get a secret value')
|
|
72
|
+
.option('-q, --quiet', 'Output only the value (for piping)')
|
|
73
|
+
.action((key, options) => {
|
|
74
|
+
getSecret(key, options);
|
|
75
|
+
});
|
|
76
|
+
secrets
|
|
77
|
+
.command('get-for-repo <keys...>')
|
|
78
|
+
.description('Get multiple secrets needed by a repo')
|
|
79
|
+
.option('--json', 'Output as JSON')
|
|
80
|
+
.action((keys, options) => {
|
|
81
|
+
getSecretsForRepo(keys, options);
|
|
82
|
+
});
|
|
66
83
|
secrets
|
|
67
84
|
.command('delete <key>')
|
|
68
85
|
.description('Delete a secret')
|
|
@@ -86,6 +103,89 @@ Examples:
|
|
|
86
103
|
haystack config sandbox off # Disable sandbox mode
|
|
87
104
|
`)
|
|
88
105
|
.action(handleSandbox);
|
|
106
|
+
config
|
|
107
|
+
.command('agentic-tool [tool]')
|
|
108
|
+
.description('Set agentic tool (opencode|claude-code|codex|status)')
|
|
109
|
+
.addHelpText('after', `
|
|
110
|
+
Tools:
|
|
111
|
+
opencode OpenCode (Haystack billing) - default
|
|
112
|
+
claude-code Claude Code (your Claude Max subscription)
|
|
113
|
+
codex Codex CLI (your ChatGPT subscription)
|
|
114
|
+
status Show current setting (default)
|
|
115
|
+
|
|
116
|
+
This sets your account-level default. Projects can override
|
|
117
|
+
this in .haystack.json under agentic.tool.
|
|
118
|
+
|
|
119
|
+
Examples:
|
|
120
|
+
haystack config agentic-tool # Show current setting
|
|
121
|
+
haystack config agentic-tool opencode # Use Haystack billing
|
|
122
|
+
haystack config agentic-tool claude-code # Use your Claude Max
|
|
123
|
+
haystack config agentic-tool codex # Use your ChatGPT
|
|
124
|
+
`)
|
|
125
|
+
.action(handleAgenticTool);
|
|
126
|
+
// Skills subcommands
|
|
127
|
+
const skills = program
|
|
128
|
+
.command('skills')
|
|
129
|
+
.description('Manage AI skills for Claude Code');
|
|
130
|
+
skills
|
|
131
|
+
.command('install')
|
|
132
|
+
.description('Install Haystack skills into your coding CLI')
|
|
133
|
+
.option('--cli <name>', 'Target CLI: claude, codex, cursor, or manual')
|
|
134
|
+
.addHelpText('after', `
|
|
135
|
+
This registers the Haystack MCP server with your coding CLI, enabling:
|
|
136
|
+
/setup-haystack - AI-assisted project setup
|
|
137
|
+
/prepare-haystack - Add accessibility attributes
|
|
138
|
+
/setup-haystack-secrets - Configure secrets
|
|
139
|
+
|
|
140
|
+
Supported CLIs:
|
|
141
|
+
claude Claude Code (auto-detected)
|
|
142
|
+
codex Codex CLI (auto-detected)
|
|
143
|
+
cursor Cursor IDE (auto-detected)
|
|
144
|
+
manual Show manual setup instructions
|
|
145
|
+
|
|
146
|
+
Examples:
|
|
147
|
+
haystack skills install # Auto-detect and install
|
|
148
|
+
haystack skills install --cli codex # Install for Codex only
|
|
149
|
+
haystack skills install --cli manual # Show manual instructions
|
|
150
|
+
`)
|
|
151
|
+
.action(installSkills);
|
|
152
|
+
skills
|
|
153
|
+
.command('list')
|
|
154
|
+
.description('List available Haystack skills')
|
|
155
|
+
.action(listSkills);
|
|
156
|
+
// Hooks subcommands
|
|
157
|
+
const hooks = program
|
|
158
|
+
.command('hooks')
|
|
159
|
+
.description('Manage git hooks for AI agent quality checks');
|
|
160
|
+
hooks
|
|
161
|
+
.command('install')
|
|
162
|
+
.description('Install Haystack git hooks and Entire CLI')
|
|
163
|
+
.option('--version <version>', 'Entire CLI version (default: pinned)')
|
|
164
|
+
.option('-f, --force', 'Overwrite existing hooks')
|
|
165
|
+
.option('--skip-entire', 'Skip Entire binary download')
|
|
166
|
+
.addHelpText('after', `
|
|
167
|
+
This installs:
|
|
168
|
+
• Git hooks for AI agent quality checks (pre-commit, commit-msg, etc.)
|
|
169
|
+
• Agent context detector (identifies AI agent sessions)
|
|
170
|
+
• Truncation checker (prevents code truncation by LLMs)
|
|
171
|
+
• Entire CLI binary for session tracking (powered by https://entire.dev)
|
|
172
|
+
|
|
173
|
+
Hooks are installed to <repo>/hooks/ and git is configured to use them.
|
|
174
|
+
|
|
175
|
+
Examples:
|
|
176
|
+
haystack hooks install # Install with pinned Entire version
|
|
177
|
+
haystack hooks install --force # Overwrite existing hooks
|
|
178
|
+
haystack hooks install --skip-entire # Only install Haystack hooks
|
|
179
|
+
`)
|
|
180
|
+
.action(hooksInstall);
|
|
181
|
+
hooks
|
|
182
|
+
.command('status')
|
|
183
|
+
.description('Check hooks installation status')
|
|
184
|
+
.action(hooksStatus);
|
|
185
|
+
hooks
|
|
186
|
+
.command('update')
|
|
187
|
+
.description('Update Entire CLI to the latest version')
|
|
188
|
+
.action(hooksUpdate);
|
|
89
189
|
// Show help if no command provided
|
|
90
190
|
if (process.argv.length === 2) {
|
|
91
191
|
program.help();
|
package/dist/types.d.ts
CHANGED
|
@@ -34,19 +34,43 @@ export interface HaystackConfig {
|
|
|
34
34
|
*/
|
|
35
35
|
network?: NetworkConfig;
|
|
36
36
|
/**
|
|
37
|
-
* Secrets
|
|
38
|
-
*
|
|
39
|
-
*
|
|
37
|
+
* Secrets required by this project.
|
|
38
|
+
* Values are stored securely on the Haystack platform via `haystack secrets set`.
|
|
39
|
+
* At runtime, secrets are fetched and injected as environment variables.
|
|
40
40
|
*
|
|
41
|
-
*
|
|
42
|
-
* Use
|
|
41
|
+
* This field declares WHAT secrets are needed, not their values.
|
|
42
|
+
* Use `haystack secrets set KEY VALUE` to store the actual values.
|
|
43
43
|
*
|
|
44
44
|
* @example
|
|
45
45
|
* secrets:
|
|
46
|
-
*
|
|
47
|
-
*
|
|
46
|
+
* OPENAI_API_KEY:
|
|
47
|
+
* description: "OpenAI API key for LLM calls"
|
|
48
|
+
* required: true
|
|
49
|
+
* STAGING_TOKEN:
|
|
50
|
+
* description: "Token for staging API access"
|
|
51
|
+
* required: false
|
|
48
52
|
*/
|
|
49
|
-
secrets?: Record<string,
|
|
53
|
+
secrets?: Record<string, SecretDeclaration>;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Secret declaration - describes what a secret is for, not its value
|
|
57
|
+
*/
|
|
58
|
+
export interface SecretDeclaration {
|
|
59
|
+
/** Human-readable description of what this secret is used for */
|
|
60
|
+
description?: string;
|
|
61
|
+
/**
|
|
62
|
+
* Whether this secret is required for verification to run.
|
|
63
|
+
* If true and the secret is not set, verification will fail with an error.
|
|
64
|
+
* If false, verification will continue with a warning.
|
|
65
|
+
* @default false
|
|
66
|
+
*/
|
|
67
|
+
required?: boolean;
|
|
68
|
+
/**
|
|
69
|
+
* Services that use this secret.
|
|
70
|
+
* If specified, the secret is only injected into these services' environments.
|
|
71
|
+
* If omitted, the secret is available to all services.
|
|
72
|
+
*/
|
|
73
|
+
services?: string[];
|
|
50
74
|
}
|
|
51
75
|
/**
|
|
52
76
|
* Dev server configuration (simple mode)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utilities for haystack hooks install/status/update commands.
|
|
3
|
+
*
|
|
4
|
+
* Handles Entire CLI binary download, hook file copying, and git configuration.
|
|
5
|
+
*/
|
|
6
|
+
export declare const ENTIRE_DEFAULT_VERSION = "0.4.8";
|
|
7
|
+
export declare const ENTIRE_GITHUB_REPO = "entireio/cli";
|
|
8
|
+
export declare const HAYSTACK_BIN_DIR: string;
|
|
9
|
+
export declare const ENTIRE_BIN_PATH: string;
|
|
10
|
+
export declare function getPlatformAssetName(): string;
|
|
11
|
+
export declare function findGitRoot(): string | null;
|
|
12
|
+
export declare function getInstalledEntireVersion(): Promise<string | null>;
|
|
13
|
+
export declare function getLatestEntireVersion(): Promise<string>;
|
|
14
|
+
export declare function downloadEntireBinary(version: string): Promise<void>;
|
|
15
|
+
/** Resolves the path to dist/assets/hooks/ (or src/assets/hooks/ in dev) */
|
|
16
|
+
export declare function getAssetsDir(): string;
|
|
17
|
+
/** Copy hook scripts and TypeScript modules into the target hooks directory */
|
|
18
|
+
export declare function copyHookFiles(hooksDir: string): Promise<void>;
|
|
19
|
+
/** Install npm dependencies in the hooks directory */
|
|
20
|
+
export declare function installHookDeps(hooksDir: string): Promise<void>;
|
|
21
|
+
/** Copy LLM rules template if none exists */
|
|
22
|
+
export declare function copyLlmRulesTemplate(repoRoot: string): Promise<boolean>;
|
|
23
|
+
/** Create .entire/settings.json and .entire/.gitignore */
|
|
24
|
+
export declare function createEntireConfig(repoRoot: string): Promise<void>;
|
|
25
|
+
/** Add .entire/metadata/ to repo .gitignore if not already present */
|
|
26
|
+
export declare function updateGitignore(repoRoot: string): Promise<boolean>;
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utilities for haystack hooks install/status/update commands.
|
|
3
|
+
*
|
|
4
|
+
* Handles Entire CLI binary download, hook file copying, and git configuration.
|
|
5
|
+
*/
|
|
6
|
+
import * as fs from 'fs/promises';
|
|
7
|
+
import { existsSync } from 'fs';
|
|
8
|
+
import * as path from 'path';
|
|
9
|
+
import * as os from 'os';
|
|
10
|
+
import { execSync } from 'child_process';
|
|
11
|
+
import { fileURLToPath } from 'url';
|
|
12
|
+
// ============================================================================
|
|
13
|
+
// Constants
|
|
14
|
+
// ============================================================================
|
|
15
|
+
export const ENTIRE_DEFAULT_VERSION = '0.4.8';
|
|
16
|
+
export const ENTIRE_GITHUB_REPO = 'entireio/cli';
|
|
17
|
+
export const HAYSTACK_BIN_DIR = path.join(os.homedir(), '.haystack', 'bin');
|
|
18
|
+
export const ENTIRE_BIN_PATH = path.join(HAYSTACK_BIN_DIR, 'entire');
|
|
19
|
+
const HOOK_SCRIPTS = [
|
|
20
|
+
'pre-commit',
|
|
21
|
+
'commit-msg',
|
|
22
|
+
'post-commit',
|
|
23
|
+
'pre-push',
|
|
24
|
+
'prepare-commit-msg',
|
|
25
|
+
];
|
|
26
|
+
// ============================================================================
|
|
27
|
+
// Platform detection
|
|
28
|
+
// ============================================================================
|
|
29
|
+
const OS_MAP = {
|
|
30
|
+
darwin: 'darwin',
|
|
31
|
+
linux: 'linux',
|
|
32
|
+
};
|
|
33
|
+
const ARCH_MAP = {
|
|
34
|
+
arm64: 'arm64',
|
|
35
|
+
x64: 'amd64',
|
|
36
|
+
};
|
|
37
|
+
export function getPlatformAssetName() {
|
|
38
|
+
const osName = OS_MAP[process.platform];
|
|
39
|
+
const archName = ARCH_MAP[process.arch];
|
|
40
|
+
if (!osName || !archName) {
|
|
41
|
+
throw new Error(`Unsupported platform: ${process.platform}-${process.arch}. Entire CLI supports macOS and Linux (amd64/arm64).`);
|
|
42
|
+
}
|
|
43
|
+
return `entire_${osName}_${archName}.tar.gz`;
|
|
44
|
+
}
|
|
45
|
+
// ============================================================================
|
|
46
|
+
// Git repo detection
|
|
47
|
+
// ============================================================================
|
|
48
|
+
export function findGitRoot() {
|
|
49
|
+
try {
|
|
50
|
+
return execSync('git rev-parse --show-toplevel', {
|
|
51
|
+
encoding: 'utf-8',
|
|
52
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
53
|
+
}).trim();
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// ============================================================================
|
|
60
|
+
// Entire binary management
|
|
61
|
+
// ============================================================================
|
|
62
|
+
export async function getInstalledEntireVersion() {
|
|
63
|
+
if (!existsSync(ENTIRE_BIN_PATH))
|
|
64
|
+
return null;
|
|
65
|
+
try {
|
|
66
|
+
const output = execSync(`"${ENTIRE_BIN_PATH}" --version`, {
|
|
67
|
+
encoding: 'utf-8',
|
|
68
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
69
|
+
}).trim();
|
|
70
|
+
// Output is like "entire version 0.4.8" or just "0.4.8"
|
|
71
|
+
const match = output.match(/(\d+\.\d+\.\d+)/);
|
|
72
|
+
return match ? match[1] : output;
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
export async function getLatestEntireVersion() {
|
|
79
|
+
const response = await fetch(`https://api.github.com/repos/${ENTIRE_GITHUB_REPO}/releases/latest`, {
|
|
80
|
+
headers: {
|
|
81
|
+
'User-Agent': 'Haystack-CLI',
|
|
82
|
+
Accept: 'application/vnd.github.v3+json',
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
if (!response.ok) {
|
|
86
|
+
throw new Error(`Failed to fetch latest Entire release: ${response.status} ${response.statusText}`);
|
|
87
|
+
}
|
|
88
|
+
const data = (await response.json());
|
|
89
|
+
// tag_name is like "v0.4.8"
|
|
90
|
+
return data.tag_name.replace(/^v/, '');
|
|
91
|
+
}
|
|
92
|
+
export async function downloadEntireBinary(version) {
|
|
93
|
+
const assetName = getPlatformAssetName();
|
|
94
|
+
const tag = version.startsWith('v') ? version : `v${version}`;
|
|
95
|
+
// Fetch release to get asset download URL
|
|
96
|
+
const releaseResponse = await fetch(`https://api.github.com/repos/${ENTIRE_GITHUB_REPO}/releases/tags/${tag}`, {
|
|
97
|
+
headers: {
|
|
98
|
+
'User-Agent': 'Haystack-CLI',
|
|
99
|
+
Accept: 'application/vnd.github.v3+json',
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
if (!releaseResponse.ok) {
|
|
103
|
+
throw new Error(`Failed to fetch Entire release ${tag}: ${releaseResponse.status}`);
|
|
104
|
+
}
|
|
105
|
+
const release = (await releaseResponse.json());
|
|
106
|
+
const asset = release.assets.find((a) => a.name === assetName);
|
|
107
|
+
if (!asset) {
|
|
108
|
+
throw new Error(`No binary found for ${assetName} in Entire release ${tag}. Available: ${release.assets.map((a) => a.name).join(', ')}`);
|
|
109
|
+
}
|
|
110
|
+
// Download the tar.gz
|
|
111
|
+
const downloadResponse = await fetch(asset.browser_download_url, {
|
|
112
|
+
headers: { 'User-Agent': 'Haystack-CLI' },
|
|
113
|
+
});
|
|
114
|
+
if (!downloadResponse.ok) {
|
|
115
|
+
throw new Error(`Failed to download ${assetName}: ${downloadResponse.status}`);
|
|
116
|
+
}
|
|
117
|
+
const buffer = Buffer.from(await downloadResponse.arrayBuffer());
|
|
118
|
+
// Create temp dir, extract, and move binary
|
|
119
|
+
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'haystack-entire-'));
|
|
120
|
+
const tarPath = path.join(tmpDir, assetName);
|
|
121
|
+
try {
|
|
122
|
+
await fs.writeFile(tarPath, buffer);
|
|
123
|
+
execSync(`tar xzf "${tarPath}" -C "${tmpDir}"`, { stdio: 'pipe' });
|
|
124
|
+
// Ensure bin dir exists
|
|
125
|
+
await fs.mkdir(HAYSTACK_BIN_DIR, { recursive: true });
|
|
126
|
+
// Move the binary
|
|
127
|
+
const extractedBinary = path.join(tmpDir, 'entire');
|
|
128
|
+
if (!existsSync(extractedBinary)) {
|
|
129
|
+
throw new Error('Entire binary not found in extracted archive');
|
|
130
|
+
}
|
|
131
|
+
await fs.copyFile(extractedBinary, ENTIRE_BIN_PATH);
|
|
132
|
+
await fs.chmod(ENTIRE_BIN_PATH, 0o755);
|
|
133
|
+
}
|
|
134
|
+
finally {
|
|
135
|
+
// Clean up temp dir
|
|
136
|
+
await fs.rm(tmpDir, { recursive: true, force: true });
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
// ============================================================================
|
|
140
|
+
// Asset directory resolution
|
|
141
|
+
// ============================================================================
|
|
142
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
143
|
+
const __dirname = path.dirname(__filename);
|
|
144
|
+
/** Resolves the path to dist/assets/hooks/ (or src/assets/hooks/ in dev) */
|
|
145
|
+
export function getAssetsDir() {
|
|
146
|
+
// From dist/utils/hooks.js → dist/assets/hooks/
|
|
147
|
+
// From src/utils/hooks.ts → src/assets/hooks/
|
|
148
|
+
return path.resolve(__dirname, '..', 'assets', 'hooks');
|
|
149
|
+
}
|
|
150
|
+
// ============================================================================
|
|
151
|
+
// Hook file operations
|
|
152
|
+
// ============================================================================
|
|
153
|
+
/** Copy hook scripts and TypeScript modules into the target hooks directory */
|
|
154
|
+
export async function copyHookFiles(hooksDir) {
|
|
155
|
+
const assetsDir = getAssetsDir();
|
|
156
|
+
const scriptsDir = path.join(assetsDir, 'scripts');
|
|
157
|
+
// Ensure hooks directory exists
|
|
158
|
+
await fs.mkdir(hooksDir, { recursive: true });
|
|
159
|
+
// Copy shell scripts (strip .sh extension, set executable)
|
|
160
|
+
for (const hook of HOOK_SCRIPTS) {
|
|
161
|
+
const src = path.join(scriptsDir, `${hook}.sh`);
|
|
162
|
+
const dest = path.join(hooksDir, hook);
|
|
163
|
+
await fs.copyFile(src, dest);
|
|
164
|
+
await fs.chmod(dest, 0o755);
|
|
165
|
+
}
|
|
166
|
+
// Copy agent-context module
|
|
167
|
+
await copyDirRecursive(path.join(assetsDir, 'agent-context'), path.join(hooksDir, 'agent-context'));
|
|
168
|
+
// Copy truncation-checker module
|
|
169
|
+
await copyDirRecursive(path.join(assetsDir, 'truncation-checker'), path.join(hooksDir, 'truncation-checker'));
|
|
170
|
+
// Copy hooks package.json
|
|
171
|
+
await fs.copyFile(path.join(assetsDir, 'package.json'), path.join(hooksDir, 'package.json'));
|
|
172
|
+
}
|
|
173
|
+
/** Install npm dependencies in the hooks directory */
|
|
174
|
+
export async function installHookDeps(hooksDir) {
|
|
175
|
+
execSync('npm install --no-fund --no-audit', {
|
|
176
|
+
cwd: hooksDir,
|
|
177
|
+
stdio: 'pipe',
|
|
178
|
+
timeout: 60_000,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
/** Copy LLM rules template if none exists */
|
|
182
|
+
export async function copyLlmRulesTemplate(repoRoot) {
|
|
183
|
+
const dest = path.join(repoRoot, 'LLM_RULES.md');
|
|
184
|
+
if (existsSync(dest))
|
|
185
|
+
return false;
|
|
186
|
+
const src = path.join(getAssetsDir(), 'llm-rules-template.md');
|
|
187
|
+
await fs.copyFile(src, dest);
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
/** Create .entire/settings.json and .entire/.gitignore */
|
|
191
|
+
export async function createEntireConfig(repoRoot) {
|
|
192
|
+
const entireDir = path.join(repoRoot, '.entire');
|
|
193
|
+
await fs.mkdir(entireDir, { recursive: true });
|
|
194
|
+
await fs.writeFile(path.join(entireDir, 'settings.json'), JSON.stringify({ enabled: true, telemetry: false }, null, 2) + '\n', 'utf-8');
|
|
195
|
+
await fs.writeFile(path.join(entireDir, '.gitignore'), 'tmp/\nsettings.local.json\nmetadata/\nlogs/\n', 'utf-8');
|
|
196
|
+
}
|
|
197
|
+
/** Add .entire/metadata/ to repo .gitignore if not already present */
|
|
198
|
+
export async function updateGitignore(repoRoot) {
|
|
199
|
+
const gitignorePath = path.join(repoRoot, '.gitignore');
|
|
200
|
+
let content = '';
|
|
201
|
+
if (existsSync(gitignorePath)) {
|
|
202
|
+
content = await fs.readFile(gitignorePath, 'utf-8');
|
|
203
|
+
}
|
|
204
|
+
if (content.includes('.entire/metadata'))
|
|
205
|
+
return false;
|
|
206
|
+
const addition = '\n# Entire CLI local session data\n.entire/metadata/\n';
|
|
207
|
+
await fs.writeFile(gitignorePath, content + addition, 'utf-8');
|
|
208
|
+
return true;
|
|
209
|
+
}
|
|
210
|
+
// ============================================================================
|
|
211
|
+
// Helpers
|
|
212
|
+
// ============================================================================
|
|
213
|
+
async function copyDirRecursive(src, dest) {
|
|
214
|
+
await fs.mkdir(dest, { recursive: true });
|
|
215
|
+
const entries = await fs.readdir(src, { withFileTypes: true });
|
|
216
|
+
for (const entry of entries) {
|
|
217
|
+
const srcPath = path.join(src, entry.name);
|
|
218
|
+
const destPath = path.join(dest, entry.name);
|
|
219
|
+
if (entry.isDirectory()) {
|
|
220
|
+
await copyDirRecursive(srcPath, destPath);
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
await fs.copyFile(srcPath, destPath);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
package/dist/utils/skill.d.ts
CHANGED
|
@@ -5,6 +5,6 @@
|
|
|
5
5
|
export declare function createSkillFile(): Promise<string>;
|
|
6
6
|
/**
|
|
7
7
|
* Create the .claude/commands/ files for Claude Code slash commands
|
|
8
|
-
* Users can invoke with /setup-haystack or /
|
|
8
|
+
* Users can invoke with /setup-haystack, /prepare-haystack, or /setup-haystack-secrets
|
|
9
9
|
*/
|
|
10
10
|
export declare function createClaudeCommand(): Promise<string>;
|