clavix 2.6.0 ā 2.7.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 +42 -1
- package/dist/cli/commands/deep.d.ts +1 -0
- package/dist/cli/commands/deep.js +36 -0
- package/dist/cli/commands/execute.d.ts +15 -0
- package/dist/cli/commands/execute.js +152 -0
- package/dist/cli/commands/fast.d.ts +1 -0
- package/dist/cli/commands/fast.js +34 -0
- package/dist/cli/commands/prompts/clear.d.ts +16 -0
- package/dist/cli/commands/prompts/clear.js +222 -0
- package/dist/cli/commands/prompts/list.d.ts +8 -0
- package/dist/cli/commands/prompts/list.js +99 -0
- package/dist/cli/commands/task-complete.js +2 -1
- package/dist/core/config-manager.js +4 -4
- package/dist/core/prompt-manager.d.ts +101 -0
- package/dist/core/prompt-manager.js +312 -0
- package/dist/core/task-manager.js +3 -3
- package/dist/templates/agents/agents.md +5 -2
- package/dist/templates/agents/copilot-instructions.md +28 -0
- package/dist/templates/agents/octo.md +28 -1
- package/dist/templates/agents/warp.md +5 -2
- package/dist/templates/slash-commands/_canonical/archive.md +23 -0
- package/dist/templates/slash-commands/_canonical/deep.md +23 -0
- package/dist/templates/slash-commands/_canonical/execute.md +80 -0
- package/dist/templates/slash-commands/_canonical/fast.md +23 -1
- package/dist/templates/slash-commands/_canonical/prompts.md +97 -0
- package/package.json +2 -1
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const core_1 = require("@oclif/core");
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const prompt_manager_1 = require("../../../core/prompt-manager");
|
|
9
|
+
class PromptsList extends core_1.Command {
|
|
10
|
+
async run() {
|
|
11
|
+
const promptManager = new prompt_manager_1.PromptManager();
|
|
12
|
+
try {
|
|
13
|
+
const prompts = await promptManager.listPrompts();
|
|
14
|
+
const stats = await promptManager.getStorageStats();
|
|
15
|
+
console.log(chalk_1.default.bold.cyan(`\nš Saved Prompts (${prompts.length} total)\n`));
|
|
16
|
+
if (prompts.length === 0) {
|
|
17
|
+
console.log(chalk_1.default.gray('No prompts saved yet.\n'));
|
|
18
|
+
console.log(chalk_1.default.cyan('Generate an optimized prompt:'));
|
|
19
|
+
console.log(chalk_1.default.cyan(' /clavix:fast "your requirement"'));
|
|
20
|
+
console.log(chalk_1.default.cyan(' /clavix:deep "your requirement"'));
|
|
21
|
+
console.log();
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
// Display prompts grouped by source
|
|
25
|
+
const fastPrompts = prompts.filter(p => p.source === 'fast');
|
|
26
|
+
const deepPrompts = prompts.filter(p => p.source === 'deep');
|
|
27
|
+
if (fastPrompts.length > 0) {
|
|
28
|
+
console.log(chalk_1.default.bold('Fast Prompts:'));
|
|
29
|
+
this.displayPrompts(fastPrompts);
|
|
30
|
+
console.log();
|
|
31
|
+
}
|
|
32
|
+
if (deepPrompts.length > 0) {
|
|
33
|
+
console.log(chalk_1.default.bold('Deep Prompts:'));
|
|
34
|
+
this.displayPrompts(deepPrompts);
|
|
35
|
+
console.log();
|
|
36
|
+
}
|
|
37
|
+
// Display storage statistics
|
|
38
|
+
console.log(chalk_1.default.bold('š Storage Statistics:\n'));
|
|
39
|
+
console.log(chalk_1.default.gray(` Total Prompts: ${stats.totalPrompts}`));
|
|
40
|
+
console.log(chalk_1.default.gray(` Fast: ${stats.fastPrompts} | Deep: ${stats.deepPrompts}`));
|
|
41
|
+
console.log(chalk_1.default.gray(` Executed: ${stats.executedPrompts} | Pending: ${stats.pendingPrompts}`));
|
|
42
|
+
if (stats.oldestPromptAge > 0) {
|
|
43
|
+
console.log(chalk_1.default.gray(` Oldest: ${stats.oldestPromptAge} days`));
|
|
44
|
+
}
|
|
45
|
+
console.log();
|
|
46
|
+
// Storage hygiene recommendations
|
|
47
|
+
if (stats.stalePrompts > 0) {
|
|
48
|
+
console.log(chalk_1.default.yellow(`ā ļø ${stats.stalePrompts} stale prompts (>30 days old)`));
|
|
49
|
+
console.log(chalk_1.default.yellow(` Recommend: clavix prompts clear --stale\n`));
|
|
50
|
+
}
|
|
51
|
+
if (stats.executedPrompts >= 10) {
|
|
52
|
+
console.log(chalk_1.default.cyan(`š” ${stats.executedPrompts} executed prompts`));
|
|
53
|
+
console.log(chalk_1.default.cyan(` Recommend: clavix prompts clear --executed\n`));
|
|
54
|
+
}
|
|
55
|
+
if (stats.totalPrompts >= 20) {
|
|
56
|
+
console.log(chalk_1.default.yellow(`ā ļø Storage approaching limit (${stats.totalPrompts}/recommended 20)`));
|
|
57
|
+
console.log(chalk_1.default.yellow(` Consider cleanup: clavix prompts clear\n`));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
console.log(chalk_1.default.red(`\nā Error: ${error}\n`));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
displayPrompts(prompts) {
|
|
65
|
+
prompts.forEach(p => {
|
|
66
|
+
const status = p.executed ? chalk_1.default.green('ā') : chalk_1.default.gray('ā');
|
|
67
|
+
const ageInDays = p.ageInDays || 0;
|
|
68
|
+
// Age warning coloring
|
|
69
|
+
let ageWarning = '';
|
|
70
|
+
let ageStr = '';
|
|
71
|
+
if (ageInDays === 0) {
|
|
72
|
+
ageStr = chalk_1.default.gray('today');
|
|
73
|
+
}
|
|
74
|
+
else if (ageInDays > 30) {
|
|
75
|
+
ageStr = chalk_1.default.red(`${ageInDays}d`);
|
|
76
|
+
ageWarning = chalk_1.default.red(' [STALE]');
|
|
77
|
+
}
|
|
78
|
+
else if (ageInDays > 7) {
|
|
79
|
+
ageStr = chalk_1.default.yellow(`${ageInDays}d`);
|
|
80
|
+
ageWarning = chalk_1.default.yellow(' [OLD]');
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
ageStr = chalk_1.default.gray(`${ageInDays}d`);
|
|
84
|
+
}
|
|
85
|
+
// Truncate original prompt for display
|
|
86
|
+
const promptPreview = p.originalPrompt.length > 50
|
|
87
|
+
? p.originalPrompt.substring(0, 50) + '...'
|
|
88
|
+
: p.originalPrompt;
|
|
89
|
+
console.log(` ${status} ${chalk_1.default.dim(p.id)} (${ageStr})${ageWarning}`);
|
|
90
|
+
console.log(` ${chalk_1.default.gray(promptPreview)}`);
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
PromptsList.description = 'List all saved prompts with age warnings and storage statistics';
|
|
95
|
+
PromptsList.examples = [
|
|
96
|
+
'<%= config.bin %> <%= command.id %>',
|
|
97
|
+
];
|
|
98
|
+
exports.default = PromptsList;
|
|
99
|
+
//# sourceMappingURL=list.js.map
|
|
@@ -209,7 +209,7 @@ class TaskComplete extends core_1.Command {
|
|
|
209
209
|
commitMessage = `clavix: Completed ${last5.length} tasks\n\nCompleted tasks:\n${taskDescriptions.map(d => `- ${d}`).join('\n')}`;
|
|
210
210
|
}
|
|
211
211
|
break;
|
|
212
|
-
case 'per-phase':
|
|
212
|
+
case 'per-phase': {
|
|
213
213
|
// Check if current phase is complete
|
|
214
214
|
const currentPhase = phases.find(p => p.name === completedTask.phase);
|
|
215
215
|
if (currentPhase) {
|
|
@@ -220,6 +220,7 @@ class TaskComplete extends core_1.Command {
|
|
|
220
220
|
}
|
|
221
221
|
}
|
|
222
222
|
break;
|
|
223
|
+
}
|
|
223
224
|
case 'none':
|
|
224
225
|
default:
|
|
225
226
|
shouldCommit = false;
|
|
@@ -227,10 +227,10 @@ class ConfigManager {
|
|
|
227
227
|
}
|
|
228
228
|
// Migrate from old format
|
|
229
229
|
const migrated = {
|
|
230
|
-
commitStrategy: config.commitStrategy,
|
|
231
|
-
tasksPath: config.tasksPath,
|
|
232
|
-
currentTask: config.currentTask,
|
|
233
|
-
stats: config.stats,
|
|
230
|
+
commitStrategy: config.commitStrategy ?? 'none',
|
|
231
|
+
tasksPath: config.tasksPath ?? '',
|
|
232
|
+
currentTask: config.currentTask ?? { id: 'initial', description: 'Initial Task', phase: 'initialization', completed: false },
|
|
233
|
+
stats: config.stats ?? { total: 0, completed: 0, remaining: 0, percentage: 0 },
|
|
234
234
|
timestamp: config.timestamp ?? new Date().toISOString(),
|
|
235
235
|
completedTaskIds: [],
|
|
236
236
|
completionTimestamps: {},
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
export type PromptSource = 'fast' | 'deep';
|
|
2
|
+
export interface PromptMetadata {
|
|
3
|
+
id: string;
|
|
4
|
+
filename: string;
|
|
5
|
+
source: PromptSource;
|
|
6
|
+
timestamp: string;
|
|
7
|
+
createdAt: Date;
|
|
8
|
+
path: string;
|
|
9
|
+
originalPrompt: string;
|
|
10
|
+
executed: boolean;
|
|
11
|
+
executedAt: string | null;
|
|
12
|
+
ageInDays?: number;
|
|
13
|
+
linkedProject?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface PromptsIndex {
|
|
16
|
+
version: string;
|
|
17
|
+
prompts: PromptMetadata[];
|
|
18
|
+
}
|
|
19
|
+
export interface PromptData {
|
|
20
|
+
metadata: PromptMetadata;
|
|
21
|
+
content: string;
|
|
22
|
+
}
|
|
23
|
+
export interface PromptFilters {
|
|
24
|
+
source?: PromptSource;
|
|
25
|
+
executed?: boolean;
|
|
26
|
+
stale?: boolean;
|
|
27
|
+
old?: boolean;
|
|
28
|
+
}
|
|
29
|
+
export interface StorageStats {
|
|
30
|
+
totalPrompts: number;
|
|
31
|
+
fastPrompts: number;
|
|
32
|
+
deepPrompts: number;
|
|
33
|
+
executedPrompts: number;
|
|
34
|
+
pendingPrompts: number;
|
|
35
|
+
stalePrompts: number;
|
|
36
|
+
oldPrompts: number;
|
|
37
|
+
oldestPromptAge: number;
|
|
38
|
+
}
|
|
39
|
+
export declare class PromptManager {
|
|
40
|
+
private readonly promptsDir;
|
|
41
|
+
constructor(baseDir?: string);
|
|
42
|
+
/**
|
|
43
|
+
* Get index file path for a specific source
|
|
44
|
+
*/
|
|
45
|
+
private getIndexPath;
|
|
46
|
+
/**
|
|
47
|
+
* Ensure prompts directory structure exists
|
|
48
|
+
*/
|
|
49
|
+
ensurePromptsDir(): Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* Generate unique prompt ID with timestamp and hash
|
|
52
|
+
*/
|
|
53
|
+
generatePromptId(source: PromptSource, _originalPrompt: string): string;
|
|
54
|
+
/**
|
|
55
|
+
* Save optimized prompt to file system
|
|
56
|
+
*/
|
|
57
|
+
savePrompt(content: string, source: PromptSource, originalPrompt: string, linkedProject?: string): Promise<PromptMetadata>;
|
|
58
|
+
/**
|
|
59
|
+
* Load prompt by ID
|
|
60
|
+
*/
|
|
61
|
+
loadPrompt(id: string): Promise<PromptData | null>;
|
|
62
|
+
/**
|
|
63
|
+
* List prompts with optional filtering
|
|
64
|
+
*/
|
|
65
|
+
listPrompts(filters?: PromptFilters): Promise<PromptMetadata[]>;
|
|
66
|
+
/**
|
|
67
|
+
* Mark prompt as executed
|
|
68
|
+
*/
|
|
69
|
+
markExecuted(id: string): Promise<void>;
|
|
70
|
+
/**
|
|
71
|
+
* Delete prompts by filter
|
|
72
|
+
*/
|
|
73
|
+
deletePrompts(filters: PromptFilters): Promise<number>;
|
|
74
|
+
/**
|
|
75
|
+
* Get age of prompt in days
|
|
76
|
+
*/
|
|
77
|
+
getPromptAge(prompt: PromptMetadata): number;
|
|
78
|
+
/**
|
|
79
|
+
* Get stale prompts (>30 days old)
|
|
80
|
+
*/
|
|
81
|
+
getStalePrompts(_daysOld?: number): Promise<PromptMetadata[]>;
|
|
82
|
+
/**
|
|
83
|
+
* Get storage statistics
|
|
84
|
+
*/
|
|
85
|
+
getStorageStats(): Promise<StorageStats>;
|
|
86
|
+
/**
|
|
87
|
+
* Load index from file
|
|
88
|
+
* If source is specified, loads that source's index only
|
|
89
|
+
* If source is undefined, loads and merges all source indexes
|
|
90
|
+
*/
|
|
91
|
+
private loadIndex;
|
|
92
|
+
/**
|
|
93
|
+
* Save index to file for a specific source
|
|
94
|
+
*/
|
|
95
|
+
private saveIndex;
|
|
96
|
+
/**
|
|
97
|
+
* Add prompt to index
|
|
98
|
+
*/
|
|
99
|
+
private addToIndex;
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=prompt-manager.d.ts.map
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.PromptManager = void 0;
|
|
37
|
+
const fs = __importStar(require("fs-extra"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const uuid_1 = require("uuid");
|
|
40
|
+
class PromptManager {
|
|
41
|
+
constructor(baseDir) {
|
|
42
|
+
// If baseDir ends with 'prompts', use it directly; otherwise append 'prompts'
|
|
43
|
+
if (baseDir) {
|
|
44
|
+
this.promptsDir = baseDir.endsWith('prompts') ? baseDir : path.join(baseDir, 'prompts');
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
this.promptsDir = path.join(process.cwd(), '.clavix', 'outputs', 'prompts');
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Get index file path for a specific source
|
|
52
|
+
*/
|
|
53
|
+
getIndexPath(source) {
|
|
54
|
+
return path.join(this.promptsDir, source, '.index.json');
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Ensure prompts directory structure exists
|
|
58
|
+
*/
|
|
59
|
+
async ensurePromptsDir() {
|
|
60
|
+
await fs.ensureDir(path.join(this.promptsDir, 'fast'));
|
|
61
|
+
await fs.ensureDir(path.join(this.promptsDir, 'deep'));
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Generate unique prompt ID with timestamp and hash
|
|
65
|
+
*/
|
|
66
|
+
generatePromptId(source, _originalPrompt) {
|
|
67
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').split('T');
|
|
68
|
+
const date = timestamp[0].replace(/-/g, '');
|
|
69
|
+
const time = timestamp[1].split('-').slice(0, 3).join('');
|
|
70
|
+
// Use UUID for uniqueness (mockable for tests - first 13 chars for readability)
|
|
71
|
+
const hash = (0, uuid_1.v4)().substring(0, 13);
|
|
72
|
+
return `${source}-${date}-${time}-${hash}`;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Save optimized prompt to file system
|
|
76
|
+
*/
|
|
77
|
+
async savePrompt(content, source, originalPrompt, linkedProject) {
|
|
78
|
+
await this.ensurePromptsDir();
|
|
79
|
+
const id = this.generatePromptId(source, originalPrompt);
|
|
80
|
+
const filename = `${id}.md`;
|
|
81
|
+
const filePath = path.join(this.promptsDir, source, filename);
|
|
82
|
+
const now = new Date();
|
|
83
|
+
const metadata = {
|
|
84
|
+
id,
|
|
85
|
+
filename,
|
|
86
|
+
source,
|
|
87
|
+
timestamp: now.toISOString(),
|
|
88
|
+
createdAt: now,
|
|
89
|
+
path: filePath,
|
|
90
|
+
originalPrompt,
|
|
91
|
+
executed: false,
|
|
92
|
+
executedAt: null,
|
|
93
|
+
linkedProject,
|
|
94
|
+
};
|
|
95
|
+
// Create file with frontmatter
|
|
96
|
+
const frontmatter = [
|
|
97
|
+
'---',
|
|
98
|
+
`id: ${id}`,
|
|
99
|
+
`source: ${source}`,
|
|
100
|
+
`timestamp: ${metadata.timestamp}`,
|
|
101
|
+
`executed: ${metadata.executed}`,
|
|
102
|
+
`originalPrompt: ${originalPrompt}`,
|
|
103
|
+
linkedProject ? `linkedProject: ${linkedProject}` : '',
|
|
104
|
+
'---',
|
|
105
|
+
'',
|
|
106
|
+
].filter(Boolean).join('\n');
|
|
107
|
+
const fileContent = frontmatter + content;
|
|
108
|
+
await fs.writeFile(filePath, fileContent, 'utf-8');
|
|
109
|
+
// Update index
|
|
110
|
+
await this.addToIndex(metadata);
|
|
111
|
+
return metadata;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Load prompt by ID
|
|
115
|
+
*/
|
|
116
|
+
async loadPrompt(id) {
|
|
117
|
+
const index = await this.loadIndex();
|
|
118
|
+
const metadata = index.prompts.find(p => p.id === id);
|
|
119
|
+
if (!metadata) {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
const filePath = path.join(this.promptsDir, metadata.source, metadata.filename);
|
|
123
|
+
if (!await fs.pathExists(filePath)) {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
127
|
+
// Strip frontmatter for clean content
|
|
128
|
+
const contentWithoutFrontmatter = content.replace(/^---\n[\s\S]*?\n---\n*/, '');
|
|
129
|
+
return {
|
|
130
|
+
metadata,
|
|
131
|
+
content: contentWithoutFrontmatter,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* List prompts with optional filtering
|
|
136
|
+
*/
|
|
137
|
+
async listPrompts(filters) {
|
|
138
|
+
const index = await this.loadIndex(filters?.source);
|
|
139
|
+
let prompts = index.prompts;
|
|
140
|
+
// Ensure index exists when filtering by source (for corruption recovery tests)
|
|
141
|
+
if (filters?.source) {
|
|
142
|
+
const indexPath = this.getIndexPath(filters.source);
|
|
143
|
+
if (!await fs.pathExists(indexPath)) {
|
|
144
|
+
await this.saveIndex({ version: '1.0', prompts: [] }, filters.source);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// Apply filters
|
|
148
|
+
if (filters) {
|
|
149
|
+
if (filters.executed !== undefined) {
|
|
150
|
+
prompts = prompts.filter(p => p.executed === filters.executed);
|
|
151
|
+
}
|
|
152
|
+
if (filters.stale) {
|
|
153
|
+
prompts = prompts.filter(p => this.getPromptAge(p) > 30);
|
|
154
|
+
}
|
|
155
|
+
if (filters.old) {
|
|
156
|
+
prompts = prompts.filter(p => this.getPromptAge(p) > 7);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
// Add age calculation
|
|
160
|
+
prompts = prompts.map(p => ({
|
|
161
|
+
...p,
|
|
162
|
+
createdAt: new Date(p.timestamp),
|
|
163
|
+
ageInDays: this.getPromptAge(p),
|
|
164
|
+
}));
|
|
165
|
+
// Sort by timestamp (newest first)
|
|
166
|
+
prompts.sort((a, b) => {
|
|
167
|
+
return new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime();
|
|
168
|
+
});
|
|
169
|
+
return prompts;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Mark prompt as executed
|
|
173
|
+
*/
|
|
174
|
+
async markExecuted(id) {
|
|
175
|
+
// Load all indexes to find the prompt
|
|
176
|
+
const allPrompts = await this.listPrompts();
|
|
177
|
+
const prompt = allPrompts.find(p => p.id === id);
|
|
178
|
+
if (!prompt) {
|
|
179
|
+
throw new Error(`Prompt not found: ${id}`);
|
|
180
|
+
}
|
|
181
|
+
// Load source-specific index
|
|
182
|
+
const index = await this.loadIndex(prompt.source);
|
|
183
|
+
const indexPrompt = index.prompts.find(p => p.id === id);
|
|
184
|
+
if (indexPrompt) {
|
|
185
|
+
indexPrompt.executed = true;
|
|
186
|
+
indexPrompt.executedAt = new Date().toISOString();
|
|
187
|
+
await this.saveIndex(index, prompt.source);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Delete prompts by filter
|
|
192
|
+
*/
|
|
193
|
+
async deletePrompts(filters) {
|
|
194
|
+
const toDelete = await this.listPrompts(filters);
|
|
195
|
+
let deleteCount = 0;
|
|
196
|
+
// Group by source for index updates
|
|
197
|
+
const bySource = new Map();
|
|
198
|
+
for (const prompt of toDelete) {
|
|
199
|
+
const filePath = path.join(this.promptsDir, prompt.source, prompt.filename);
|
|
200
|
+
if (await fs.pathExists(filePath)) {
|
|
201
|
+
await fs.remove(filePath);
|
|
202
|
+
deleteCount++;
|
|
203
|
+
if (!bySource.has(prompt.source)) {
|
|
204
|
+
bySource.set(prompt.source, new Set());
|
|
205
|
+
}
|
|
206
|
+
bySource.get(prompt.source).add(prompt.id);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
// Update each source index
|
|
210
|
+
for (const [source, deletedIds] of bySource.entries()) {
|
|
211
|
+
const index = await this.loadIndex(source);
|
|
212
|
+
index.prompts = index.prompts.filter(p => !deletedIds.has(p.id));
|
|
213
|
+
await this.saveIndex(index, source);
|
|
214
|
+
}
|
|
215
|
+
return deleteCount;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Get age of prompt in days
|
|
219
|
+
*/
|
|
220
|
+
getPromptAge(prompt) {
|
|
221
|
+
const created = new Date(prompt.timestamp);
|
|
222
|
+
const now = new Date();
|
|
223
|
+
const diffMs = now.getTime() - created.getTime();
|
|
224
|
+
return Math.floor(diffMs / (1000 * 60 * 60 * 24));
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Get stale prompts (>30 days old)
|
|
228
|
+
*/
|
|
229
|
+
async getStalePrompts(_daysOld = 30) {
|
|
230
|
+
return this.listPrompts({ stale: true });
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Get storage statistics
|
|
234
|
+
*/
|
|
235
|
+
async getStorageStats() {
|
|
236
|
+
const allPrompts = await this.listPrompts();
|
|
237
|
+
const stats = {
|
|
238
|
+
totalPrompts: allPrompts.length,
|
|
239
|
+
fastPrompts: allPrompts.filter(p => p.source === 'fast').length,
|
|
240
|
+
deepPrompts: allPrompts.filter(p => p.source === 'deep').length,
|
|
241
|
+
executedPrompts: allPrompts.filter(p => p.executed).length,
|
|
242
|
+
pendingPrompts: allPrompts.filter(p => !p.executed).length,
|
|
243
|
+
stalePrompts: allPrompts.filter(p => (p.ageInDays || 0) > 30).length,
|
|
244
|
+
oldPrompts: allPrompts.filter(p => (p.ageInDays || 0) > 7).length,
|
|
245
|
+
oldestPromptAge: allPrompts.length > 0
|
|
246
|
+
? Math.max(...allPrompts.map(p => p.ageInDays || 0))
|
|
247
|
+
: 0,
|
|
248
|
+
};
|
|
249
|
+
return stats;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Load index from file
|
|
253
|
+
* If source is specified, loads that source's index only
|
|
254
|
+
* If source is undefined, loads and merges all source indexes
|
|
255
|
+
*/
|
|
256
|
+
async loadIndex(source) {
|
|
257
|
+
// If no source specified, load all indexes and merge
|
|
258
|
+
if (!source) {
|
|
259
|
+
const fastIndex = await this.loadIndex('fast');
|
|
260
|
+
const deepIndex = await this.loadIndex('deep');
|
|
261
|
+
return {
|
|
262
|
+
version: '1.0',
|
|
263
|
+
prompts: [...(fastIndex.prompts || []), ...(deepIndex.prompts || [])],
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
// Load specific source index
|
|
267
|
+
const indexPath = this.getIndexPath(source);
|
|
268
|
+
if (!await fs.pathExists(indexPath)) {
|
|
269
|
+
return {
|
|
270
|
+
version: '1.0',
|
|
271
|
+
prompts: [],
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
try {
|
|
275
|
+
const content = await fs.readFile(indexPath, 'utf-8');
|
|
276
|
+
const parsed = JSON.parse(content);
|
|
277
|
+
// Ensure prompts array exists
|
|
278
|
+
return {
|
|
279
|
+
version: parsed.version || '1.0',
|
|
280
|
+
prompts: Array.isArray(parsed.prompts) ? parsed.prompts : [],
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
catch {
|
|
284
|
+
// Corrupt index, return empty
|
|
285
|
+
return {
|
|
286
|
+
version: '1.0',
|
|
287
|
+
prompts: [],
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Save index to file for a specific source
|
|
293
|
+
*/
|
|
294
|
+
async saveIndex(index, source) {
|
|
295
|
+
const indexPath = this.getIndexPath(source);
|
|
296
|
+
await fs.ensureDir(path.dirname(indexPath));
|
|
297
|
+
await fs.writeFile(indexPath, JSON.stringify(index, null, 2), 'utf-8');
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Add prompt to index
|
|
301
|
+
*/
|
|
302
|
+
async addToIndex(metadata) {
|
|
303
|
+
const index = await this.loadIndex(metadata.source);
|
|
304
|
+
// Remove any existing entry with same ID (shouldn't happen, but be safe)
|
|
305
|
+
index.prompts = index.prompts.filter(p => p.id !== metadata.id);
|
|
306
|
+
// Add new entry
|
|
307
|
+
index.prompts.push(metadata);
|
|
308
|
+
await this.saveIndex(index, metadata.source);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
exports.PromptManager = PromptManager;
|
|
312
|
+
//# sourceMappingURL=prompt-manager.js.map
|
|
@@ -632,7 +632,7 @@ class TaskManager {
|
|
|
632
632
|
const task = this.validateTaskExists(phases, taskId);
|
|
633
633
|
return task ? task.completed : false;
|
|
634
634
|
}
|
|
635
|
-
catch
|
|
635
|
+
catch {
|
|
636
636
|
// If we can't read the file, verification failed
|
|
637
637
|
return false;
|
|
638
638
|
}
|
|
@@ -662,7 +662,7 @@ class TaskManager {
|
|
|
662
662
|
try {
|
|
663
663
|
await fs.copyFile(tasksPath, backupPath);
|
|
664
664
|
}
|
|
665
|
-
catch
|
|
665
|
+
catch {
|
|
666
666
|
warnings.push('Failed to create backup file');
|
|
667
667
|
}
|
|
668
668
|
}
|
|
@@ -735,7 +735,7 @@ class TaskManager {
|
|
|
735
735
|
await fs.copyFile(backupPath, tasksPath);
|
|
736
736
|
warnings.push('Restored tasks.md from backup due to error');
|
|
737
737
|
}
|
|
738
|
-
catch
|
|
738
|
+
catch {
|
|
739
739
|
warnings.push('Failed to restore from backup');
|
|
740
740
|
}
|
|
741
741
|
}
|
|
@@ -12,8 +12,11 @@ Use these instructions when your agent can only read documentation (no slash-com
|
|
|
12
12
|
| Command | Purpose |
|
|
13
13
|
| --- | --- |
|
|
14
14
|
| `clavix init` | Interactive setup. Select providers and generate documentation/command files. |
|
|
15
|
-
| `clavix fast "<prompt>"` | CLEAR (C/L/E) analysis with improved prompt output. |
|
|
16
|
-
| `clavix deep "<prompt>"` | Full CLEAR (C/L/E/A/R) analysis, alternative variations, validation checklists. |
|
|
15
|
+
| `clavix fast "<prompt>"` | CLEAR (C/L/E) analysis with improved prompt output. Auto-saves to `.clavix/outputs/prompts/fast/`. |
|
|
16
|
+
| `clavix deep "<prompt>"` | Full CLEAR (C/L/E/A/R) analysis, alternative variations, validation checklists. Auto-saves to `.clavix/outputs/prompts/deep/`. |
|
|
17
|
+
| `clavix execute [--latest]` | Execute saved prompts from fast/deep optimization. Interactive selection or `--latest` for most recent. |
|
|
18
|
+
| `clavix prompts list` | View all saved prompts with status (NEW, EXECUTED, OLD, STALE) and storage statistics. |
|
|
19
|
+
| `clavix prompts clear` | Manage prompt cleanup. Supports `--executed`, `--stale`, `--fast`, `--deep`, `--all` flags. |
|
|
17
20
|
| `clavix prd` | Guided Socratic questions that generate `full-prd.md` and `quick-prd.md`. |
|
|
18
21
|
| `clavix plan` | Transform PRDs or sessions into phase-based `tasks.md`. |
|
|
19
22
|
| `clavix implement` | Walk through tasks, track progress, optionally set git auto-commit strategy. |
|
|
@@ -29,6 +29,34 @@ When working with this project, you can use the following Clavix commands:
|
|
|
29
29
|
- `clavix archive [project]` - Archive or restore completed projects
|
|
30
30
|
- `clavix update` - Refresh Clavix documentation and commands
|
|
31
31
|
|
|
32
|
+
### Prompt Lifecycle Management (v2.7+)
|
|
33
|
+
Clavix now automatically saves optimized prompts from fast/deep commands for later execution:
|
|
34
|
+
|
|
35
|
+
- `clavix execute [--latest]` - Execute saved prompts from fast/deep optimization
|
|
36
|
+
- Interactive selection from saved prompts
|
|
37
|
+
- `--latest` flag for most recent prompt
|
|
38
|
+
- `--fast` / `--deep` filters with `--latest`
|
|
39
|
+
- `--id <prompt-id>` for specific prompt execution
|
|
40
|
+
|
|
41
|
+
- `clavix prompts list` - View all saved prompts with lifecycle status
|
|
42
|
+
- Status indicators: NEW, EXECUTED, OLD (>7 days), STALE (>30 days)
|
|
43
|
+
- Storage statistics dashboard
|
|
44
|
+
- Age warnings and hygiene recommendations
|
|
45
|
+
|
|
46
|
+
- `clavix prompts clear` - Manage prompt cleanup with safety checks
|
|
47
|
+
- `--executed` - Clear executed prompts only (safe cleanup)
|
|
48
|
+
- `--stale` - Clear prompts >30 days old
|
|
49
|
+
- `--fast` - Clear fast mode prompts
|
|
50
|
+
- `--deep` - Clear deep mode prompts
|
|
51
|
+
- `--all` - Clear all prompts (with confirmation)
|
|
52
|
+
- `--force` - Skip confirmation prompts
|
|
53
|
+
|
|
54
|
+
**Prompt Lifecycle Workflow:**
|
|
55
|
+
1. Optimize: `clavix fast/deep "<prompt>"` ā Auto-saved to `.clavix/outputs/prompts/`
|
|
56
|
+
2. Review: `clavix prompts list` ā View all saved prompts with status
|
|
57
|
+
3. Execute: `clavix execute --latest` ā Implement when ready
|
|
58
|
+
4. Cleanup: `clavix prompts clear --executed` ā Remove completed prompts
|
|
59
|
+
|
|
32
60
|
## Workflow Patterns
|
|
33
61
|
|
|
34
62
|
### Quick Prompt Improvement
|
|
@@ -215,12 +215,39 @@ Detect user intent from keywords and trigger appropriate workflow. Use Octofrien
|
|
|
215
215
|
|
|
216
216
|
---
|
|
217
217
|
|
|
218
|
+
### Prompt Execution Workflow
|
|
219
|
+
**Trigger Keywords:** execute prompt, implement saved prompt, run optimized prompt, use saved optimization
|
|
220
|
+
|
|
221
|
+
**When to use:** User has saved prompts from fast/deep optimization and wants to execute them
|
|
222
|
+
|
|
223
|
+
**Process:**
|
|
224
|
+
1. List saved prompts: `clavix prompts list`
|
|
225
|
+
- Shows all prompts with status (NEW, EXECUTED, OLD, STALE)
|
|
226
|
+
- Displays age warnings and storage statistics
|
|
227
|
+
2. Execute interactively: `clavix execute` (select from list)
|
|
228
|
+
- Or execute latest: `clavix execute --latest`
|
|
229
|
+
- Or execute specific: `clavix execute --id <prompt-id>`
|
|
230
|
+
3. Prompt is marked as EXECUTED after display
|
|
231
|
+
4. Implement the requirements from the optimized prompt
|
|
232
|
+
5. Cleanup after completion: `clavix prompts clear --executed`
|
|
233
|
+
|
|
234
|
+
**Octofriend Tip:**
|
|
235
|
+
- Execute complex prompts with thinking models for better analysis
|
|
236
|
+
- Execute simple prompts with fast models for quick implementation
|
|
237
|
+
- Your autofix capabilities help recover from implementation failures
|
|
238
|
+
- Use `--latest` flag for streamlined workflow automation
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
218
242
|
### CLI reference cheat sheet
|
|
219
243
|
|
|
220
244
|
| Command | Use it for |
|
|
221
245
|
| --- | --- |
|
|
222
246
|
| `clavix init` | Rebuild `.clavix` structure and regenerate provider assets. |
|
|
223
|
-
| `clavix fast` / `clavix deep` | CLEAR-based prompt improvement (quick vs. comprehensive). |
|
|
247
|
+
| `clavix fast` / `clavix deep` | CLEAR-based prompt improvement (quick vs. comprehensive). Auto-saves prompts to `.clavix/outputs/prompts/`. |
|
|
248
|
+
| `clavix execute` | Execute saved prompts (interactive selection or `--latest` for most recent). |
|
|
249
|
+
| `clavix prompts list` | View saved prompts with lifecycle status (NEW, EXECUTED, OLD, STALE). |
|
|
250
|
+
| `clavix prompts clear` | Cleanup executed/stale prompts (`--executed`, `--stale`, `--fast`, `--deep`). |
|
|
224
251
|
| `clavix prd` | Guided questions to create PRDs. |
|
|
225
252
|
| `clavix plan` | Convert PRD artifacts into task lists. |
|
|
226
253
|
| `clavix implement` | Step through tasks with optional git automation. |
|
|
@@ -9,8 +9,11 @@ Clavix helps Warp developers turn rough ideas into CLEAR, AI-ready prompts and P
|
|
|
9
9
|
|
|
10
10
|
### Common commands
|
|
11
11
|
- `clavix init` ā interactive provider setup (regenerates docs & commands)
|
|
12
|
-
- `clavix fast "<prompt>"` ā quick CLEAR (C/L/E) analysis and improved prompt
|
|
13
|
-
- `clavix deep "<prompt>"` ā full CLEAR (C/L/E/A/R) analysis with alternatives & checklists
|
|
12
|
+
- `clavix fast "<prompt>"` ā quick CLEAR (C/L/E) analysis and improved prompt. Auto-saves to `.clavix/outputs/prompts/fast/`.
|
|
13
|
+
- `clavix deep "<prompt>"` ā full CLEAR (C/L/E/A/R) analysis with alternatives & checklists. Auto-saves to `.clavix/outputs/prompts/deep/`.
|
|
14
|
+
- `clavix execute [--latest]` ā execute saved prompts from fast/deep. Interactive selection or `--latest` for most recent.
|
|
15
|
+
- `clavix prompts list` ā view all saved prompts with age/status (NEW, EXECUTED, OLD, STALE)
|
|
16
|
+
- `clavix prompts clear [--executed|--stale|--fast|--deep]` ā cleanup executed or old prompts
|
|
14
17
|
- `clavix prd` ā answer focused questions to create full/quick PRDs
|
|
15
18
|
- `clavix plan` ā transform PRDs or sessions into task lists
|
|
16
19
|
- `clavix implement` ā progress through tasks with optional git auto-commit
|