mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-03-18 18:48:48 +08:00
Add comprehensive command and skill reference documentation in Chinese
- Created a new document for command and skill references, detailing orchestrator commands, workflow session commands, issue workflow commands, IDAW commands, with-file workflows, cycle workflows, CLI commands, memory commands, team skills, workflow skills, utility skills, and Codex capabilities. - Added a comparison table for workflows, outlining their best uses, levels, self-containment, and automatic chaining behavior.
This commit is contained in:
@@ -121,7 +121,8 @@ export class CliHistoryStore {
|
||||
this.db = new Database(this.dbPath);
|
||||
this.db.pragma('journal_mode = WAL');
|
||||
this.db.pragma('synchronous = NORMAL');
|
||||
this.db.pragma('busy_timeout = 5000'); // Wait up to 5 seconds for locks
|
||||
this.db.pragma('busy_timeout = 10000'); // Wait up to 10 seconds for locks (increased for write-heavy scenarios)
|
||||
this.db.pragma('wal_autocheckpoint = 1000'); // Optimize WAL checkpointing
|
||||
|
||||
this.initSchema();
|
||||
this.migrateFromJson(historyDir);
|
||||
@@ -386,12 +387,13 @@ export class CliHistoryStore {
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a database operation with retry logic for SQLITE_BUSY errors
|
||||
* Execute a database operation with retry logic for SQLITE_BUSY errors (async, non-blocking)
|
||||
* @param operation - Function to execute
|
||||
* @param maxRetries - Maximum retry attempts (default: 3)
|
||||
* @param baseDelay - Base delay in ms for exponential backoff (default: 100)
|
||||
*/
|
||||
private withRetry<T>(operation: () => T, maxRetries = 3, baseDelay = 100): T {
|
||||
private async withRetry<T>(operation: () => T, maxRetries = 3, baseDelay = 100): Promise<T> {
|
||||
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
|
||||
let lastError: Error | null = null;
|
||||
|
||||
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
||||
@@ -405,10 +407,8 @@ export class CliHistoryStore {
|
||||
if (attempt < maxRetries) {
|
||||
// Exponential backoff: 100ms, 200ms, 400ms
|
||||
const delay = baseDelay * Math.pow(2, attempt);
|
||||
// Sync sleep using Atomics (works in Node.js)
|
||||
const sharedBuffer = new SharedArrayBuffer(4);
|
||||
const sharedArray = new Int32Array(sharedBuffer);
|
||||
Atomics.wait(sharedArray, 0, 0, delay);
|
||||
// Async non-blocking sleep
|
||||
await sleep(delay);
|
||||
}
|
||||
} else {
|
||||
// Non-BUSY error, throw immediately
|
||||
@@ -421,7 +421,7 @@ export class CliHistoryStore {
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate existing JSON files to SQLite
|
||||
* Migrate existing JSON files to SQLite (async, non-blocking)
|
||||
*/
|
||||
private migrateFromJson(historyDir: string): void {
|
||||
const migrationMarker = join(historyDir, '.migrated');
|
||||
@@ -429,41 +429,50 @@ export class CliHistoryStore {
|
||||
return; // Already migrated
|
||||
}
|
||||
|
||||
// Find all date directories
|
||||
const dateDirs = readdirSync(historyDir).filter(d => {
|
||||
const dirPath = join(historyDir, d);
|
||||
return statSync(dirPath).isDirectory() && /^\d{4}-\d{2}-\d{2}$/.test(d);
|
||||
});
|
||||
// Fire-and-forget async migration
|
||||
(async () => {
|
||||
try {
|
||||
// Find all date directories
|
||||
const dateDirs = readdirSync(historyDir).filter(d => {
|
||||
const dirPath = join(historyDir, d);
|
||||
return statSync(dirPath).isDirectory() && /^\d{4}-\d{2}-\d{2}$/.test(d);
|
||||
});
|
||||
|
||||
let migratedCount = 0;
|
||||
let migratedCount = 0;
|
||||
|
||||
for (const dateDir of dateDirs) {
|
||||
const dirPath = join(historyDir, dateDir);
|
||||
const files = readdirSync(dirPath).filter(f => f.endsWith('.json'));
|
||||
for (const dateDir of dateDirs) {
|
||||
const dirPath = join(historyDir, dateDir);
|
||||
const files = readdirSync(dirPath).filter(f => f.endsWith('.json'));
|
||||
|
||||
for (const file of files) {
|
||||
try {
|
||||
const filePath = join(dirPath, file);
|
||||
const data = JSON.parse(readFileSync(filePath, 'utf8'));
|
||||
for (const file of files) {
|
||||
try {
|
||||
const filePath = join(dirPath, file);
|
||||
const data = JSON.parse(readFileSync(filePath, 'utf8'));
|
||||
|
||||
// Convert to conversation record if legacy format
|
||||
const conversation = this.normalizeRecord(data);
|
||||
this.saveConversation(conversation);
|
||||
migratedCount++;
|
||||
// Convert to conversation record if legacy format
|
||||
const conversation = this.normalizeRecord(data);
|
||||
await this.saveConversation(conversation);
|
||||
migratedCount++;
|
||||
|
||||
// Optionally delete the JSON file after migration
|
||||
// unlinkSync(filePath);
|
||||
} catch (err) {
|
||||
console.error(`Failed to migrate ${file}:`, (err as Error).message);
|
||||
// Optionally delete the JSON file after migration
|
||||
// unlinkSync(filePath);
|
||||
} catch (err) {
|
||||
console.error(`Failed to migrate ${file}:`, (err as Error).message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create migration marker
|
||||
if (migratedCount > 0) {
|
||||
require('fs').writeFileSync(migrationMarker, new Date().toISOString());
|
||||
console.log(`[CLI History] Migrated ${migratedCount} records to SQLite`);
|
||||
}
|
||||
// Create migration marker
|
||||
if (migratedCount > 0) {
|
||||
require('fs').writeFileSync(migrationMarker, new Date().toISOString());
|
||||
console.log(`[CLI History] Migrated ${migratedCount} records to SQLite`);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[CLI History] Migration failed:', (err as Error).message);
|
||||
}
|
||||
})().catch(err => {
|
||||
console.error('[CLI History] Migration error:', (err as Error).message);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -499,9 +508,9 @@ export class CliHistoryStore {
|
||||
}
|
||||
|
||||
/**
|
||||
* Save or update a conversation
|
||||
* Save or update a conversation (async for non-blocking retry)
|
||||
*/
|
||||
saveConversation(conversation: ConversationRecord): void {
|
||||
async saveConversation(conversation: ConversationRecord): Promise<void> {
|
||||
// Ensure prompt is a string before calling substring
|
||||
const lastTurn = conversation.turns.length > 0 ? conversation.turns[conversation.turns.length - 1] : null;
|
||||
const rawPrompt = lastTurn?.prompt ?? '';
|
||||
@@ -579,7 +588,7 @@ export class CliHistoryStore {
|
||||
}
|
||||
});
|
||||
|
||||
this.withRetry(() => transaction());
|
||||
await this.withRetry(() => transaction());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -852,11 +861,11 @@ export class CliHistoryStore {
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a conversation
|
||||
* Delete a conversation (async for non-blocking retry)
|
||||
*/
|
||||
deleteConversation(id: string): { success: boolean; error?: string } {
|
||||
async deleteConversation(id: string): Promise<{ success: boolean; error?: string }> {
|
||||
try {
|
||||
const result = this.withRetry(() =>
|
||||
const result = await this.withRetry(() =>
|
||||
this.db.prepare('DELETE FROM conversations WHERE id = ?').run(id)
|
||||
);
|
||||
return { success: result.changes > 0 };
|
||||
@@ -866,9 +875,9 @@ export class CliHistoryStore {
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch delete conversations
|
||||
* Batch delete conversations (async for non-blocking retry)
|
||||
*/
|
||||
batchDelete(ids: string[]): { success: boolean; deleted: number; errors?: string[] } {
|
||||
async batchDelete(ids: string[]): Promise<{ success: boolean; deleted: number; errors?: string[] }> {
|
||||
const deleteStmt = this.db.prepare('DELETE FROM conversations WHERE id = ?');
|
||||
const errors: string[] = [];
|
||||
let deleted = 0;
|
||||
@@ -884,7 +893,7 @@ export class CliHistoryStore {
|
||||
}
|
||||
});
|
||||
|
||||
this.withRetry(() => transaction());
|
||||
await this.withRetry(() => transaction());
|
||||
|
||||
return {
|
||||
success: true,
|
||||
@@ -947,9 +956,9 @@ export class CliHistoryStore {
|
||||
// ========== Native Session Mapping Methods ==========
|
||||
|
||||
/**
|
||||
* Save or update native session mapping
|
||||
* Save or update native session mapping (async for non-blocking retry)
|
||||
*/
|
||||
saveNativeSessionMapping(mapping: NativeSessionMapping): void {
|
||||
async saveNativeSessionMapping(mapping: NativeSessionMapping): Promise<void> {
|
||||
const stmt = this.db.prepare(`
|
||||
INSERT INTO native_session_mapping (ccw_id, tool, native_session_id, native_session_path, project_hash, transaction_id, created_at)
|
||||
VALUES (@ccw_id, @tool, @native_session_id, @native_session_path, @project_hash, @transaction_id, @created_at)
|
||||
@@ -960,7 +969,7 @@ export class CliHistoryStore {
|
||||
transaction_id = @transaction_id
|
||||
`);
|
||||
|
||||
this.withRetry(() => stmt.run({
|
||||
await this.withRetry(() => stmt.run({
|
||||
ccw_id: mapping.ccw_id,
|
||||
tool: mapping.tool,
|
||||
native_session_id: mapping.native_session_id,
|
||||
@@ -1144,7 +1153,7 @@ export class CliHistoryStore {
|
||||
|
||||
// Persist mapping for future loads (best-effort).
|
||||
try {
|
||||
this.saveNativeSessionMapping({
|
||||
await this.saveNativeSessionMapping({
|
||||
ccw_id: ccwId,
|
||||
tool,
|
||||
native_session_id: best.sessionId,
|
||||
@@ -1290,9 +1299,9 @@ export class CliHistoryStore {
|
||||
// ========== Insights Methods ==========
|
||||
|
||||
/**
|
||||
* Save an insights analysis result
|
||||
* Save an insights analysis result (async for non-blocking retry)
|
||||
*/
|
||||
saveInsight(insight: {
|
||||
async saveInsight(insight: {
|
||||
id: string;
|
||||
tool: string;
|
||||
promptCount: number;
|
||||
@@ -1301,13 +1310,13 @@ export class CliHistoryStore {
|
||||
rawOutput?: string;
|
||||
executionId?: string;
|
||||
lang?: string;
|
||||
}): void {
|
||||
}): Promise<void> {
|
||||
const stmt = this.db.prepare(`
|
||||
INSERT OR REPLACE INTO insights (id, created_at, tool, prompt_count, patterns, suggestions, raw_output, execution_id, lang)
|
||||
VALUES (@id, @created_at, @tool, @prompt_count, @patterns, @suggestions, @raw_output, @execution_id, @lang)
|
||||
`);
|
||||
|
||||
this.withRetry(() => stmt.run({
|
||||
await this.withRetry(() => stmt.run({
|
||||
id: insight.id,
|
||||
created_at: new Date().toISOString(),
|
||||
tool: insight.tool,
|
||||
@@ -1391,9 +1400,9 @@ export class CliHistoryStore {
|
||||
}
|
||||
|
||||
/**
|
||||
* Save or update a review for an execution
|
||||
* Save or update a review for an execution (async for non-blocking retry)
|
||||
*/
|
||||
saveReview(review: Omit<ReviewRecord, 'id' | 'created_at' | 'updated_at'> & { created_at?: string; updated_at?: string }): ReviewRecord {
|
||||
async saveReview(review: Omit<ReviewRecord, 'id' | 'created_at' | 'updated_at'> & { created_at?: string; updated_at?: string }): Promise<ReviewRecord> {
|
||||
const now = new Date().toISOString();
|
||||
const created_at = review.created_at || now;
|
||||
const updated_at = review.updated_at || now;
|
||||
@@ -1409,7 +1418,7 @@ export class CliHistoryStore {
|
||||
updated_at = @updated_at
|
||||
`);
|
||||
|
||||
const result = this.withRetry(() => stmt.run({
|
||||
const result = await this.withRetry(() => stmt.run({
|
||||
execution_id: review.execution_id,
|
||||
status: review.status,
|
||||
rating: review.rating ?? null,
|
||||
|
||||
Reference in New Issue
Block a user