ccraft 1.0.11 → 1.0.13
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 +22 -11
- package/bin/claude-craft.js +3 -12
- package/package.json +1 -1
- package/src/commands/install.js +376 -187
- package/src/commands/update.js +1 -12
- package/src/ui/phase-header.js +9 -3
- package/src/ui/tasks.js +12 -32
- package/src/utils/analysis-cache.js +2 -32
- package/src/utils/api-client.js +6 -8
- package/src/utils/api-file-writer.js +2 -6
- package/src/commands/create.js +0 -568
- package/src/utils/claude-scorer.js +0 -101
package/src/commands/update.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* calls /api/update to get delta components, and installs only what's new.
|
|
4
4
|
*
|
|
5
5
|
* Flow:
|
|
6
|
-
* 1. Load stored
|
|
6
|
+
* 1. Load stored analysis from .claude/.claude-craft/
|
|
7
7
|
* 2. Re-run project analysis (detectProject + analyzeWithClaude)
|
|
8
8
|
* 3. Extract installed relative paths from manifest
|
|
9
9
|
* 4. Call /api/update — server returns only new/unlocked components
|
|
@@ -16,7 +16,6 @@ import ora from 'ora';
|
|
|
16
16
|
import { detectProject } from '../utils/detect-project.js';
|
|
17
17
|
import { analyzeWithClaude } from '../utils/claude-analyzer.js';
|
|
18
18
|
import {
|
|
19
|
-
readUserProfile,
|
|
20
19
|
readPermanentAnalysis,
|
|
21
20
|
readInstalledManifest,
|
|
22
21
|
mergePermanentManifest,
|
|
@@ -135,18 +134,9 @@ export async function runUpdate(options = {}) {
|
|
|
135
134
|
});
|
|
136
135
|
|
|
137
136
|
// ── Guard: must have a previous install ───────────────────────────
|
|
138
|
-
const storedProfile = readUserProfile(targetDir);
|
|
139
137
|
const previousAnalysis = readPermanentAnalysis(targetDir);
|
|
140
138
|
const installedManifest = readInstalledManifest(targetDir);
|
|
141
139
|
|
|
142
|
-
if (!storedProfile) {
|
|
143
|
-
logger.error(
|
|
144
|
-
'No stored user profile found. Re-run: ' + chalk.bold('ccraft install') +
|
|
145
|
-
' to rebuild the profile cache.',
|
|
146
|
-
);
|
|
147
|
-
process.exit(1);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
140
|
if (!previousAnalysis) {
|
|
151
141
|
logger.error(
|
|
152
142
|
'No stored project analysis found. Re-run: ' + chalk.bold('ccraft install') +
|
|
@@ -231,7 +221,6 @@ export async function runUpdate(options = {}) {
|
|
|
231
221
|
let updateResponse;
|
|
232
222
|
try {
|
|
233
223
|
updateResponse = await callUpdate(
|
|
234
|
-
storedProfile,
|
|
235
224
|
currentProjectInfo,
|
|
236
225
|
previousAnalysis,
|
|
237
226
|
installedRelativePaths,
|
package/src/ui/phase-header.js
CHANGED
|
@@ -4,11 +4,17 @@ import { PHASES, TOTAL_PHASES, getTerminalWidth } from './theme.js';
|
|
|
4
4
|
/**
|
|
5
5
|
* Render a phase header like:
|
|
6
6
|
* ── Phase 2 of 5 ── Project Discovery ──────────────────
|
|
7
|
+
*
|
|
8
|
+
* @param {number} phaseNumber — current phase number
|
|
9
|
+
* @param {object} [opts] — optional overrides
|
|
10
|
+
* @param {number} [opts.totalPhases] — override total phase count (default: TOTAL_PHASES)
|
|
11
|
+
* @param {string} [opts.name] — override phase name
|
|
7
12
|
*/
|
|
8
|
-
export function renderPhaseHeader(phaseNumber) {
|
|
13
|
+
export function renderPhaseHeader(phaseNumber, opts = {}) {
|
|
14
|
+
const total = opts.totalPhases || TOTAL_PHASES;
|
|
9
15
|
const phase = PHASES.find((p) => p.number === phaseNumber);
|
|
10
|
-
const name = phase ? phase.name : `Phase ${phaseNumber}
|
|
11
|
-
const label = `Phase ${phaseNumber} of ${
|
|
16
|
+
const name = opts.name || (phase ? phase.name : `Phase ${phaseNumber}`);
|
|
17
|
+
const label = `Phase ${phaseNumber} of ${total}`;
|
|
12
18
|
|
|
13
19
|
const w = Math.min(getTerminalWidth() - 4, 56);
|
|
14
20
|
const inner = ` ${label} ── ${name} `;
|
package/src/ui/tasks.js
CHANGED
|
@@ -104,7 +104,6 @@ export async function runAnalysisTasks(targetDir, { analyzeWithClaude, detectPro
|
|
|
104
104
|
*/
|
|
105
105
|
export async function runInstallTasks({
|
|
106
106
|
apiResponse,
|
|
107
|
-
selectedCandidateIds,
|
|
108
107
|
targetDir,
|
|
109
108
|
selectedMcps,
|
|
110
109
|
mcpKeys,
|
|
@@ -112,43 +111,24 @@ export async function runInstallTasks({
|
|
|
112
111
|
detected,
|
|
113
112
|
buildFileList,
|
|
114
113
|
writeApiFiles,
|
|
115
|
-
scoreWithClaude,
|
|
116
114
|
}) {
|
|
117
115
|
const ctx = {};
|
|
118
116
|
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
const taskList = [];
|
|
123
|
-
|
|
124
|
-
if (hasScoring) {
|
|
125
|
-
taskList.push({
|
|
126
|
-
title: `Evaluating ${candidateCount} optional candidates with Claude`,
|
|
117
|
+
const taskList = [
|
|
118
|
+
{
|
|
119
|
+
title: 'Writing configuration files',
|
|
127
120
|
task: async (ctx) => {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
121
|
+
ctx.filesToWrite = buildFileList(apiResponse);
|
|
122
|
+
ctx.results = await writeApiFiles(ctx.filesToWrite, targetDir, {
|
|
123
|
+
force: true,
|
|
124
|
+
selectedMcpIds: selectedMcps.map((m) => m.id),
|
|
125
|
+
mcpKeys,
|
|
126
|
+
securityConfig,
|
|
127
|
+
detected,
|
|
128
|
+
});
|
|
134
129
|
},
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
taskList.push({
|
|
139
|
-
title: 'Writing configuration files',
|
|
140
|
-
task: async (ctx) => {
|
|
141
|
-
const ids = ctx.selectedCandidateIds ?? selectedCandidateIds;
|
|
142
|
-
ctx.filesToWrite = buildFileList(apiResponse, ids);
|
|
143
|
-
ctx.results = await writeApiFiles(ctx.filesToWrite, targetDir, {
|
|
144
|
-
force: true,
|
|
145
|
-
selectedMcpIds: selectedMcps.map((m) => m.id),
|
|
146
|
-
mcpKeys,
|
|
147
|
-
securityConfig,
|
|
148
|
-
detected,
|
|
149
|
-
});
|
|
150
130
|
},
|
|
151
|
-
|
|
131
|
+
];
|
|
152
132
|
|
|
153
133
|
const tasks = createTaskRunner(taskList);
|
|
154
134
|
await tasks.run(ctx);
|
|
@@ -16,7 +16,7 @@ const CACHE_DIR = '.claude/.claude-craft-temp';
|
|
|
16
16
|
const PERMANENT_DIR = '.claude/.claude-craft';
|
|
17
17
|
|
|
18
18
|
/** Files promoted from temp → permanent after install. */
|
|
19
|
-
const PERMANENT_FILES = ['project-analysis.json', 'project-context.md', 'installed-manifest.json'
|
|
19
|
+
const PERMANENT_FILES = ['project-analysis.json', 'project-context.md', 'installed-manifest.json'];
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
22
|
* Resolve the temp cache directory path for a given target directory.
|
|
@@ -315,37 +315,7 @@ export function formatRichProjectContext(projectInfo, detected) {
|
|
|
315
315
|
return lines.join('\n').trimEnd();
|
|
316
316
|
}
|
|
317
317
|
|
|
318
|
-
|
|
319
|
-
* Write user profile to the temp cache directory.
|
|
320
|
-
* Called alongside writeAnalysisCache during install.
|
|
321
|
-
*
|
|
322
|
-
* @param {string} targetDir - Project root
|
|
323
|
-
* @param {object} userProfile - { intents, sourceControl, documentTools }
|
|
324
|
-
*/
|
|
325
|
-
export function writeUserProfile(targetDir, userProfile) {
|
|
326
|
-
const dir = cachePath(targetDir);
|
|
327
|
-
mkdirSync(dir, { recursive: true });
|
|
328
|
-
writeFileSync(join(dir, 'user-profile.json'), JSON.stringify(userProfile, null, 2), 'utf8');
|
|
329
|
-
logger.debug('User profile written to analysis cache.');
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
/**
|
|
333
|
-
* Read the stored user profile from the permanent .claude/.claude-craft/ directory.
|
|
334
|
-
* Returns null if not found.
|
|
335
|
-
*
|
|
336
|
-
* @param {string} targetDir - Project root
|
|
337
|
-
* @returns {object|null}
|
|
338
|
-
*/
|
|
339
|
-
export function readUserProfile(targetDir) {
|
|
340
|
-
const filePath = join(permanentPath(targetDir), 'user-profile.json');
|
|
341
|
-
try {
|
|
342
|
-
if (!existsSync(filePath)) return null;
|
|
343
|
-
return JSON.parse(readFileSync(filePath, 'utf8'));
|
|
344
|
-
} catch (err) {
|
|
345
|
-
logger.debug(`Failed to read user profile: ${err.message}`);
|
|
346
|
-
return null;
|
|
347
|
-
}
|
|
348
|
-
}
|
|
318
|
+
// writeUserProfile and readUserProfile removed — user profile no longer used.
|
|
349
319
|
|
|
350
320
|
/**
|
|
351
321
|
* Read the permanent project analysis JSON.
|
package/src/utils/api-client.js
CHANGED
|
@@ -45,12 +45,11 @@ export function saveConfig(config) {
|
|
|
45
45
|
/**
|
|
46
46
|
* Call POST /api/generate on the server.
|
|
47
47
|
*
|
|
48
|
-
* @param {object} profile - { intents, sourceControl, documentTools }
|
|
49
48
|
* @param {object} analysis - Project analysis data
|
|
50
|
-
* @param {object} [options] - {
|
|
49
|
+
* @param {object} [options] - { projectPath }
|
|
51
50
|
* @returns {Promise<{ files, summary, mcpConfigs, serverVersion }>}
|
|
52
51
|
*/
|
|
53
|
-
export async function callGenerate(
|
|
52
|
+
export async function callGenerate(analysis, options = {}) {
|
|
54
53
|
const config = loadConfig();
|
|
55
54
|
if (!config?.apiKey) {
|
|
56
55
|
throw new ApiError(
|
|
@@ -75,7 +74,7 @@ export async function callGenerate(profile, analysis, options = {}) {
|
|
|
75
74
|
'X-Claude-Craft-Version': VERSION,
|
|
76
75
|
'X-Claude-Craft-Api-Version': '1',
|
|
77
76
|
},
|
|
78
|
-
body: JSON.stringify({
|
|
77
|
+
body: JSON.stringify({ analysis, options }),
|
|
79
78
|
signal: controller.signal,
|
|
80
79
|
});
|
|
81
80
|
|
|
@@ -131,13 +130,12 @@ export async function callGenerate(profile, analysis, options = {}) {
|
|
|
131
130
|
* Call POST /api/update on the server.
|
|
132
131
|
* Returns delta components (new files not already installed) + change summary.
|
|
133
132
|
*
|
|
134
|
-
* @param {object} profile - { intents, sourceControl, documentTools }
|
|
135
133
|
* @param {object} currentAnalysis - Freshly computed project analysis
|
|
136
134
|
* @param {object} previousAnalysis - Previously stored project analysis
|
|
137
135
|
* @param {string[]} installedRelativePaths - Relative file paths already on disk
|
|
138
|
-
* @returns {Promise<{ changes, guaranteed,
|
|
136
|
+
* @returns {Promise<{ changes, guaranteed, mcpConfigs, summary }>}
|
|
139
137
|
*/
|
|
140
|
-
export async function callUpdate(
|
|
138
|
+
export async function callUpdate(currentAnalysis, previousAnalysis, installedRelativePaths) {
|
|
141
139
|
const config = loadConfig();
|
|
142
140
|
if (!config?.apiKey) {
|
|
143
141
|
throw new ApiError(
|
|
@@ -162,7 +160,7 @@ export async function callUpdate(profile, currentAnalysis, previousAnalysis, ins
|
|
|
162
160
|
'X-Claude-Craft-Version': VERSION,
|
|
163
161
|
'X-Claude-Craft-Api-Version': '1',
|
|
164
162
|
},
|
|
165
|
-
body: JSON.stringify({
|
|
163
|
+
body: JSON.stringify({ currentAnalysis, previousAnalysis, installedPaths: installedRelativePaths }),
|
|
166
164
|
signal: controller.signal,
|
|
167
165
|
});
|
|
168
166
|
|
|
@@ -132,19 +132,15 @@ export async function writeApiFiles(files, targetDir, opts = {}) {
|
|
|
132
132
|
/**
|
|
133
133
|
* Build a flat file list from a V3 API response.
|
|
134
134
|
*
|
|
135
|
-
* @param {object} apiResponse -
|
|
136
|
-
* @param {string[]|null} selectedCandidateIds - IDs selected by Claude, or null for all
|
|
135
|
+
* @param {object} apiResponse - Response with guaranteed.files and candidates.items
|
|
137
136
|
* @returns {Array<{relativePath: string, content: string, type: string}>}
|
|
138
137
|
*/
|
|
139
|
-
export function buildFileList(apiResponse
|
|
138
|
+
export function buildFileList(apiResponse) {
|
|
140
139
|
const files = [...(apiResponse.guaranteed?.files || [])];
|
|
141
140
|
|
|
142
141
|
const candidates = apiResponse.candidates?.items || [];
|
|
143
|
-
const selectedSet = selectedCandidateIds ? new Set(selectedCandidateIds) : null;
|
|
144
142
|
|
|
145
143
|
for (const candidate of candidates) {
|
|
146
|
-
// Skip unselected candidates (null = include all)
|
|
147
|
-
if (selectedSet && !selectedSet.has(candidate.id)) continue;
|
|
148
144
|
|
|
149
145
|
if (Array.isArray(candidate.files) && candidate.files.length > 0) {
|
|
150
146
|
// Multi-file candidates (skills with references/)
|