@haystackeditor/cli 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +105 -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 +56 -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 +25 -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/assets/skills/prepare-haystack.md +323 -0
- package/dist/assets/skills/secrets.md +164 -0
- package/dist/assets/skills/setup-external-sandbox.md +243 -0
- package/dist/assets/skills/setup-haystack.md +639 -0
- package/dist/assets/skills/submit.md +154 -0
- package/dist/assets/templates/CLAUDE.md.snippet +42 -0
- package/dist/assets/templates/haystack.yml +193 -0
- package/dist/commands/check-pending.d.ts +19 -0
- package/dist/commands/check-pending.js +217 -0
- package/dist/commands/config.d.ts +18 -12
- package/dist/commands/config.js +327 -52
- package/dist/commands/hooks.d.ts +17 -0
- package/dist/commands/hooks.js +269 -0
- package/dist/commands/install-session-hooks.d.ts +16 -0
- package/dist/commands/install-session-hooks.js +302 -0
- package/dist/commands/login.js +1 -1
- package/dist/commands/policy.d.ts +31 -0
- package/dist/commands/policy.js +365 -0
- package/dist/commands/skills.d.ts +8 -0
- package/dist/commands/skills.js +80 -0
- package/dist/commands/submit.d.ts +22 -0
- package/dist/commands/submit.js +428 -0
- package/dist/commands/triage.d.ts +16 -0
- package/dist/commands/triage.js +354 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +317 -2
- package/dist/tools/detect.d.ts +50 -0
- package/dist/tools/detect.js +853 -0
- package/dist/tools/fixtures.d.ts +38 -0
- package/dist/tools/fixtures.js +199 -0
- package/dist/tools/setup.d.ts +43 -0
- package/dist/tools/setup.js +597 -0
- package/dist/triage/prompts.d.ts +31 -0
- package/dist/triage/prompts.js +266 -0
- package/dist/triage/runner.d.ts +21 -0
- package/dist/triage/runner.js +325 -0
- package/dist/triage/traces.d.ts +20 -0
- package/dist/triage/traces.js +305 -0
- package/dist/triage/types.d.ts +47 -0
- package/dist/triage/types.js +7 -0
- package/dist/types.d.ts +1387 -191
- package/dist/types.js +254 -2
- package/dist/utils/analysis-api.d.ts +108 -0
- package/dist/utils/analysis-api.js +194 -0
- package/dist/utils/config.js +1 -1
- package/dist/utils/git.d.ts +80 -0
- package/dist/utils/git.js +302 -0
- package/dist/utils/github-api.d.ts +83 -0
- package/dist/utils/github-api.js +266 -0
- package/dist/utils/hooks.d.ts +26 -0
- package/dist/utils/hooks.js +226 -0
- package/dist/utils/pending-state.d.ts +38 -0
- package/dist/utils/pending-state.js +86 -0
- package/dist/utils/secrets.js +3 -3
- package/dist/utils/skill.d.ts +1 -1
- package/dist/utils/skill.js +658 -1
- package/package.json +5 -3
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pending submit state persistence.
|
|
3
|
+
*
|
|
4
|
+
* Manages `.haystack/pending-submit.json` in the git root directory.
|
|
5
|
+
* This file persists across CLI sessions so that analysis results
|
|
6
|
+
* can be retrieved after disconnection.
|
|
7
|
+
*/
|
|
8
|
+
import type { AnalysisVerdict } from './analysis-api.js';
|
|
9
|
+
export interface PendingSubmit {
|
|
10
|
+
version: 1;
|
|
11
|
+
prNumber: number;
|
|
12
|
+
prUrl: string;
|
|
13
|
+
owner: string;
|
|
14
|
+
repo: string;
|
|
15
|
+
branch: string;
|
|
16
|
+
baseBranch: string;
|
|
17
|
+
submittedAt: string;
|
|
18
|
+
status: 'polling' | 'completed' | 'failed' | 'stale';
|
|
19
|
+
analysisResult?: AnalysisVerdict;
|
|
20
|
+
resolvedAt?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Save pending submit state to disk.
|
|
24
|
+
*/
|
|
25
|
+
export declare function savePendingSubmit(state: PendingSubmit, gitRoot?: string): void;
|
|
26
|
+
/**
|
|
27
|
+
* Load pending submit state from disk.
|
|
28
|
+
* Returns null if no pending state exists or if it's stale.
|
|
29
|
+
*/
|
|
30
|
+
export declare function loadPendingSubmit(gitRoot?: string): PendingSubmit | null;
|
|
31
|
+
/**
|
|
32
|
+
* Clear pending submit state (delete the file).
|
|
33
|
+
*/
|
|
34
|
+
export declare function clearPendingSubmit(gitRoot?: string): void;
|
|
35
|
+
/**
|
|
36
|
+
* Update the status and optionally the analysis result of a pending submit.
|
|
37
|
+
*/
|
|
38
|
+
export declare function updatePendingSubmit(updates: Partial<Pick<PendingSubmit, 'status' | 'analysisResult' | 'resolvedAt'>>, gitRoot?: string): void;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pending submit state persistence.
|
|
3
|
+
*
|
|
4
|
+
* Manages `.haystack/pending-submit.json` in the git root directory.
|
|
5
|
+
* This file persists across CLI sessions so that analysis results
|
|
6
|
+
* can be retrieved after disconnection.
|
|
7
|
+
*/
|
|
8
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from 'fs';
|
|
9
|
+
import { join } from 'path';
|
|
10
|
+
import { findGitRoot } from './git.js';
|
|
11
|
+
// ============================================================================
|
|
12
|
+
// Constants
|
|
13
|
+
// ============================================================================
|
|
14
|
+
const PENDING_FILE = 'pending-submit.json';
|
|
15
|
+
const HAYSTACK_DIR = '.haystack';
|
|
16
|
+
const STALE_THRESHOLD_MS = 2 * 60 * 60 * 1000; // 2 hours
|
|
17
|
+
// ============================================================================
|
|
18
|
+
// Functions
|
|
19
|
+
// ============================================================================
|
|
20
|
+
function getPendingPath(gitRoot) {
|
|
21
|
+
const root = gitRoot || findGitRoot();
|
|
22
|
+
if (!root)
|
|
23
|
+
return null;
|
|
24
|
+
return join(root, HAYSTACK_DIR, PENDING_FILE);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Save pending submit state to disk.
|
|
28
|
+
*/
|
|
29
|
+
export function savePendingSubmit(state, gitRoot) {
|
|
30
|
+
const root = gitRoot || findGitRoot();
|
|
31
|
+
if (!root)
|
|
32
|
+
throw new Error('Not a git repository');
|
|
33
|
+
const dir = join(root, HAYSTACK_DIR);
|
|
34
|
+
if (!existsSync(dir)) {
|
|
35
|
+
mkdirSync(dir, { recursive: true });
|
|
36
|
+
}
|
|
37
|
+
const filePath = join(dir, PENDING_FILE);
|
|
38
|
+
writeFileSync(filePath, JSON.stringify(state, null, 2));
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Load pending submit state from disk.
|
|
42
|
+
* Returns null if no pending state exists or if it's stale.
|
|
43
|
+
*/
|
|
44
|
+
export function loadPendingSubmit(gitRoot) {
|
|
45
|
+
const filePath = getPendingPath(gitRoot);
|
|
46
|
+
if (!filePath || !existsSync(filePath))
|
|
47
|
+
return null;
|
|
48
|
+
try {
|
|
49
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
50
|
+
const state = JSON.parse(content);
|
|
51
|
+
// Check version
|
|
52
|
+
if (state.version !== 1)
|
|
53
|
+
return null;
|
|
54
|
+
// Auto-mark stale entries
|
|
55
|
+
if (state.status === 'polling') {
|
|
56
|
+
const age = Date.now() - new Date(state.submittedAt).getTime();
|
|
57
|
+
if (age > STALE_THRESHOLD_MS) {
|
|
58
|
+
state.status = 'stale';
|
|
59
|
+
savePendingSubmit(state, gitRoot);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return state;
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Clear pending submit state (delete the file).
|
|
70
|
+
*/
|
|
71
|
+
export function clearPendingSubmit(gitRoot) {
|
|
72
|
+
const filePath = getPendingPath(gitRoot);
|
|
73
|
+
if (filePath && existsSync(filePath)) {
|
|
74
|
+
unlinkSync(filePath);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Update the status and optionally the analysis result of a pending submit.
|
|
79
|
+
*/
|
|
80
|
+
export function updatePendingSubmit(updates, gitRoot) {
|
|
81
|
+
const state = loadPendingSubmit(gitRoot);
|
|
82
|
+
if (!state)
|
|
83
|
+
return;
|
|
84
|
+
Object.assign(state, updates);
|
|
85
|
+
savePendingSubmit(state, gitRoot);
|
|
86
|
+
}
|
package/dist/utils/secrets.js
CHANGED
|
@@ -18,7 +18,7 @@ const SECRET_PATTERNS = [
|
|
|
18
18
|
// API Keys
|
|
19
19
|
{
|
|
20
20
|
name: 'generic-api-key',
|
|
21
|
-
pattern: /(?:api[_-]?key|apikey)\s*[:=]\s*['"]?([a-zA-Z0-9_
|
|
21
|
+
pattern: /(?:api[_-]?key|apikey)\s*[:=]\s*['"]?([a-zA-Z0-9_-]{20,})['"]?/gi,
|
|
22
22
|
description: 'Potential API key detected',
|
|
23
23
|
severity: 'high',
|
|
24
24
|
},
|
|
@@ -43,7 +43,7 @@ const SECRET_PATTERNS = [
|
|
|
43
43
|
},
|
|
44
44
|
{
|
|
45
45
|
name: 'cloudflare-api-token',
|
|
46
|
-
pattern: /(?:cloudflare|cf)[_-]?(?:api)?[_-]?token\s*[:=]\s*['"]?([a-zA-Z0-9_
|
|
46
|
+
pattern: /(?:cloudflare|cf)[_-]?(?:api)?[_-]?token\s*[:=]\s*['"]?([a-zA-Z0-9_-]{40,})['"]?/gi,
|
|
47
47
|
description: 'Cloudflare API Token detected',
|
|
48
48
|
severity: 'high',
|
|
49
49
|
},
|
|
@@ -122,7 +122,7 @@ const SECRET_PATTERNS = [
|
|
|
122
122
|
// Generic high-entropy strings in config values
|
|
123
123
|
{
|
|
124
124
|
name: 'high-entropy-string',
|
|
125
|
-
pattern: /(?:token|key|secret|password|credential|auth)\s*[:=]\s*['"]([a-zA-Z0-9+/=_
|
|
125
|
+
pattern: /(?:token|key|secret|password|credential|auth)\s*[:=]\s*['"]([a-zA-Z0-9+/=_-]{32,})['"]?/gi,
|
|
126
126
|
description: 'High-entropy string in sensitive field',
|
|
127
127
|
severity: 'medium',
|
|
128
128
|
},
|
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>;
|