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.
@@ -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 user profile + analysis from .claude/.claude-craft/
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,
@@ -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 ${TOTAL_PHASES}`;
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 candidateCount = apiResponse.candidates?.items?.length || 0;
120
- const hasScoring = apiResponse.prompts?.scoring && candidateCount > 0;
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
- const scoreResult = await scoreWithClaude(apiResponse.prompts.scoring, targetDir);
129
- if (scoreResult.selected) {
130
- ctx.selectedCandidateIds = scoreResult.selected;
131
- } else {
132
- ctx.selectedCandidateIds = null;
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', 'user-profile.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.
@@ -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] - { preset }
49
+ * @param {object} [options] - { projectPath }
51
50
  * @returns {Promise<{ files, summary, mcpConfigs, serverVersion }>}
52
51
  */
53
- export async function callGenerate(profile, analysis, options = {}) {
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({ profile, analysis, options }),
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, candidates, prompts, mcpConfigs, summary, serverVersion }>}
136
+ * @returns {Promise<{ changes, guaranteed, mcpConfigs, summary }>}
139
137
  */
140
- export async function callUpdate(profile, currentAnalysis, previousAnalysis, installedRelativePaths) {
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({ profile, currentAnalysis, previousAnalysis, installedRelativePaths }),
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 - V3 response with guaranteed.files and candidates.items
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, selectedCandidateIds) {
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/)