declare-cc 0.4.8 → 0.5.2

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/bin/install.js CHANGED
@@ -1636,11 +1636,13 @@ function install(isGlobal, runtime = 'claude') {
1636
1636
  function finishInstall(settingsPath, settings, statuslineCommand, shouldInstallStatusline, runtime = 'claude', isGlobal = true) {
1637
1637
  const isOpencode = runtime === 'opencode';
1638
1638
 
1639
- if (shouldInstallStatusline && !isOpencode) {
1640
- settings.statusLine = {
1641
- type: 'command',
1642
- command: statuslineCommand
1643
- };
1639
+ // Statusline is a global UI element — only configure it for global installs.
1640
+ // Local installs must not write statusLine: it would point to a project-specific
1641
+ // path that breaks when the project moves or is deleted.
1642
+ // Users who only do local installs should run `npx declare-cc --claude --global`
1643
+ // once to get the statusline, or configure it manually.
1644
+ if (shouldInstallStatusline && !isOpencode && isGlobal) {
1645
+ settings.statusLine = { type: 'command', command: statuslineCommand };
1644
1646
  console.log(` ${green}✓${reset} Configured statusline`);
1645
1647
  }
1646
1648
 
@@ -1657,9 +1659,12 @@ function finishInstall(settingsPath, settings, statuslineCommand, shouldInstallS
1657
1659
  if (runtime === 'gemini') program = 'Gemini';
1658
1660
 
1659
1661
  const command = isOpencode ? '/declare-help' : '/declare:help';
1662
+ const statuslineNote = (!isGlobal && !isOpencode)
1663
+ ? `\n ${yellow}Tip:${reset} For the context-window statusline, run once globally:\n ${dim}npx declare-cc --claude --global${reset}\n`
1664
+ : '';
1660
1665
  console.log(`
1661
1666
  ${green}Done!${reset} Launch ${program} and run ${cyan}${command}${reset}.
1662
-
1667
+ ${statuslineNote}
1663
1668
  ${cyan}Docs & source:${reset} https://github.com/decocms/declare-cc
1664
1669
  `);
1665
1670
  }
@@ -1329,7 +1329,7 @@ var require_help = __commonJS({
1329
1329
  usage: "/declare:help"
1330
1330
  }
1331
1331
  ],
1332
- version: "0.4.7"
1332
+ version: "0.5.1"
1333
1333
  };
1334
1334
  }
1335
1335
  module2.exports = { runHelp: runHelp2 };
@@ -2987,6 +2987,13 @@ var require_sync_status = __commonJS({
2987
2987
  actionResults.push({ id: action.id, milestone: m.id, changed: false, reason: "already DONE" });
2988
2988
  continue;
2989
2989
  }
2990
+ const summaryPath = join(folderPath, `${action.id}-SUMMARY.md`);
2991
+ if (existsSync(summaryPath)) {
2992
+ planContent = updateActionStatus(planContent, action.id, "DONE");
2993
+ planDirty = true;
2994
+ actionResults.push({ id: action.id, milestone: m.id, changed: true, reason: "SUMMARY.md exists" });
2995
+ continue;
2996
+ }
2990
2997
  if (milestoneAlreadyDone) {
2991
2998
  planContent = updateActionStatus(planContent, action.id, "DONE");
2992
2999
  planDirty = true;
@@ -203,24 +203,103 @@ function renderStatusBar() {
203
203
 
204
204
  // ─── Node element builder ─────────────────────────────────────────────────────
205
205
 
206
+ const COMPLETED = new Set(['DONE','KEPT','HONORED']);
207
+ const IN_PROGRESS_STORED = new Set(['ACTIVE']);
208
+
209
+ /**
210
+ * Compute derived workflow status for a milestone from its action statuses.
211
+ * This overrides the stored MILESTONES.md status so the dashboard always
212
+ * reflects reality even if sync-status hasn't been called.
213
+ *
214
+ * @param {{ id: string, status: string, hasPlan: boolean }} milestone
215
+ * @param {Array<{ id: string, status: string, causes: string[] }>} allActions
216
+ * @returns {{ displayStatus: string, doneCount: number, totalCount: number }}
217
+ */
218
+ function deriveMilestoneStatus(milestone, allActions) {
219
+ // Authoritative integrity/terminal states — always trust these
220
+ if (['KEPT','HONORED','BROKEN','RENEGOTIATED'].includes(milestone.status)) {
221
+ const myActions = allActions.filter(a => (a.causes||[]).includes(milestone.id));
222
+ return { displayStatus: milestone.status, doneCount: myActions.filter(a=>COMPLETED.has(a.status)).length, totalCount: myActions.length };
223
+ }
224
+
225
+ const myActions = allActions.filter(a => (a.causes||[]).includes(milestone.id));
226
+ const doneCount = myActions.filter(a => COMPLETED.has(a.status)).length;
227
+ const totalCount = myActions.length;
228
+
229
+ let displayStatus;
230
+ if (totalCount === 0) {
231
+ displayStatus = milestone.hasPlan ? 'PLANNED' : 'PENDING';
232
+ } else if (doneCount === totalCount) {
233
+ displayStatus = 'DONE';
234
+ } else if (doneCount > 0) {
235
+ displayStatus = 'EXECUTING';
236
+ } else {
237
+ displayStatus = 'PLANNED';
238
+ }
239
+
240
+ return { displayStatus, doneCount, totalCount };
241
+ }
242
+
243
+ /**
244
+ * Compute derived workflow status for a declaration from its milestone statuses.
245
+ * @param {{ id: string, status: string, milestones: string[] }} declaration
246
+ * @param {Array<{ id: string, displayStatus: string }>} enrichedMilestones
247
+ * @returns {string}
248
+ */
249
+ function deriveDeclarationStatus(declaration, enrichedMilestones) {
250
+ if (['KEPT','HONORED','BROKEN','RENEGOTIATED'].includes(declaration.status)) return declaration.status;
251
+
252
+ const myMilestones = enrichedMilestones.filter(m => (declaration.milestones||[]).includes(m.id));
253
+ if (myMilestones.length === 0) return 'PENDING';
254
+
255
+ const doneCount = myMilestones.filter(m => COMPLETED.has(m.displayStatus)).length;
256
+ const executingCount = myMilestones.filter(m => m.displayStatus === 'EXECUTING').length;
257
+ const plannedCount = myMilestones.filter(m => m.displayStatus === 'PLANNED').length;
258
+
259
+ if (doneCount === myMilestones.length) return 'DONE';
260
+ if (executingCount > 0 || doneCount > 0) return 'EXECUTING';
261
+ if (plannedCount > 0) return 'PLANNED';
262
+ return 'PENDING';
263
+ }
264
+
206
265
  /**
207
266
  * Build a node DOM element.
208
- * @param {{ id: string, title?: string, statement?: string, status?: string }} item
267
+ * @param {object} item
209
268
  * @param {'declaration'|'milestone'|'action'} type
269
+ * @param {{ displayStatus?: string, doneCount?: number, totalCount?: number }} [derived]
210
270
  * @returns {HTMLElement}
211
271
  */
212
- function buildNodeEl(item, type) {
272
+ function buildNodeEl(item, type, derived = {}) {
273
+ const displayStatus = derived.displayStatus || item.status || 'PENDING';
213
274
  const el = document.createElement('div');
214
- el.className = `node node-${type} status-${statusClass(item.status || 'pending')}`;
215
- el.dataset.nodeId = item.id;
275
+ el.className = `node node-${type} status-${statusClass(displayStatus)}`;
276
+ el.dataset.nodeId = item.id;
216
277
  el.dataset.nodeType = type;
217
278
 
218
279
  const title = item.title || item.statement || item.id;
219
280
 
281
+ // Progress bar for milestones with actions
282
+ let progressHtml = '';
283
+ if (type === 'milestone' && derived.totalCount > 0) {
284
+ const pct = Math.round((derived.doneCount / derived.totalCount) * 100);
285
+ const countLabel = `${derived.doneCount}/${derived.totalCount}`;
286
+ progressHtml = `
287
+ <div class="node-progress" title="${countLabel} actions done">
288
+ <div class="node-progress-fill" style="width:${pct}%"></div>
289
+ </div>`;
290
+ }
291
+
292
+ // Badge label — show progress count for executing milestones
293
+ let badgeLabel = displayStatus;
294
+ if (type === 'milestone' && displayStatus === 'EXECUTING' && derived.totalCount > 0) {
295
+ badgeLabel = `${derived.doneCount}/${derived.totalCount} DONE`;
296
+ }
297
+
220
298
  el.innerHTML = `
221
299
  <div class="node-id">${item.id}</div>
222
300
  <div class="node-title">${truncate(title, 55)}</div>
223
- <span class="status-badge">${item.status || 'PENDING'}</span>
301
+ <span class="status-badge">${badgeLabel}</span>
302
+ ${progressHtml}
224
303
  `;
225
304
 
226
305
  el.addEventListener('click', () => selectNode(item.id, type));
@@ -234,22 +313,37 @@ function renderGraph() {
234
313
 
235
314
  const { declarations, milestones, actions } = graphData;
236
315
 
316
+ // ── Compute derived statuses from action data (always reflects reality) ──────
317
+ // Milestones
318
+ const enrichedMilestones = (milestones || []).map(m => ({
319
+ ...m,
320
+ ...deriveMilestoneStatus(m, actions || []),
321
+ }));
322
+
323
+ // Declarations
324
+ const enrichedDeclarations = (declarations || []).map(d => ({
325
+ ...d,
326
+ displayStatus: deriveDeclarationStatus(d, enrichedMilestones),
327
+ }));
328
+
237
329
  // Clear containers
238
330
  $nodesDecls.innerHTML = '';
239
331
  $nodesMiles.innerHTML = '';
240
332
  $nodesActs.innerHTML = '';
241
333
 
242
- // Render declarations
243
- (declarations || []).forEach(d => {
244
- $nodesDecls.appendChild(buildNodeEl(d, 'declaration'));
334
+ // Render
335
+ enrichedDeclarations.forEach(d => {
336
+ $nodesDecls.appendChild(buildNodeEl(d, 'declaration', { displayStatus: d.displayStatus }));
245
337
  });
246
338
 
247
- // Render milestones
248
- (milestones || []).forEach(m => {
249
- $nodesMiles.appendChild(buildNodeEl(m, 'milestone'));
339
+ enrichedMilestones.forEach(m => {
340
+ $nodesMiles.appendChild(buildNodeEl(m, 'milestone', {
341
+ displayStatus: m.displayStatus,
342
+ doneCount: m.doneCount,
343
+ totalCount: m.totalCount,
344
+ }));
250
345
  });
251
346
 
252
- // Render actions
253
347
  (actions || []).forEach(a => {
254
348
  $nodesActs.appendChild(buildNodeEl(a, 'action'));
255
349
  });
@@ -1230,7 +1324,13 @@ const COMPLETED_STATES = new Set(['DONE', 'KEPT', 'HONORED']);
1230
1324
  function checkProjectComplete(graph) {
1231
1325
  if (confettiFired) return;
1232
1326
  if (!graph || !graph.declarations || graph.declarations.length === 0) return;
1233
- const allDone = graph.declarations.every(d => COMPLETED_STATES.has(d.status));
1327
+ // Use derived statuses (computed from actions) not stored MILESTONES.md status
1328
+ const enriched = (graph.milestones || []).map(m => ({
1329
+ ...m, ...deriveMilestoneStatus(m, graph.actions || []),
1330
+ }));
1331
+ const allDone = graph.declarations.every(d =>
1332
+ COMPLETED_STATES.has(deriveDeclarationStatus(d, enriched))
1333
+ );
1234
1334
  if (!allDone) return;
1235
1335
  confettiFired = true;
1236
1336
  fireConfetti();
@@ -41,6 +41,15 @@
41
41
  --act-done-bg: #08180f;
42
42
  --act-done-border: #123428;
43
43
 
44
+ /* workflow progress tones */
45
+ --planned-color: #5ba3ff;
46
+ --planned-bg: #091828;
47
+ --planned-border: #12305a;
48
+
49
+ --executing-color: #fbbf24;
50
+ --executing-bg: #1a1200;
51
+ --executing-border:#3d2c00;
52
+
44
53
  --broken-color: #ff4d6d;
45
54
  --broken-bg: #2a0a10;
46
55
  --broken-border: #5a1520;
@@ -264,6 +273,41 @@
264
273
  opacity: 0.82;
265
274
  }
266
275
 
276
+ /* Workflow progress states — computed from action data, not MILESTONES.md */
277
+ .node.status-planned {
278
+ background: var(--planned-bg);
279
+ border-color: var(--planned-border);
280
+ color: var(--planned-color);
281
+ opacity: 0.9;
282
+ }
283
+ .node.status-executing {
284
+ background: var(--executing-bg);
285
+ border-color: var(--executing-border);
286
+ color: var(--executing-color);
287
+ box-shadow: 0 0 0 1px var(--executing-border), 0 0 12px rgba(251,191,36,0.15);
288
+ }
289
+ .node.status-planned .node-title { color: var(--planned-color); }
290
+ .node.status-executing .node-title { color: var(--executing-color); }
291
+
292
+ /* Progress bar inside milestone node */
293
+ .node-progress {
294
+ margin-top: 7px;
295
+ height: 3px;
296
+ background: rgba(255,255,255,0.08);
297
+ border-radius: 2px;
298
+ overflow: hidden;
299
+ }
300
+ .node-progress-fill {
301
+ height: 100%;
302
+ border-radius: 2px;
303
+ transition: width 0.4s ease;
304
+ }
305
+ .status-executing .node-progress-fill { background: var(--executing-color); }
306
+ .status-planned .node-progress-fill { background: var(--planned-color); opacity:0.3; }
307
+ .status-done .node-progress-fill,
308
+ .status-kept .node-progress-fill,
309
+ .status-honored .node-progress-fill { background: var(--act-done-color); }
310
+
267
311
  .node.status-broken {
268
312
  background: var(--broken-bg);
269
313
  border-color: var(--broken-border);
@@ -0,0 +1,106 @@
1
+ #!/usr/bin/env node
2
+ // Declare activity hook — PreToolUse + PostToolUse
3
+ // Writes interesting tool events to .planning/activity.jsonl
4
+ // Server watches .planning/ via fs.watch and pushes SSE events to dashboard.
5
+ //
6
+ // Installed for PreToolUse and PostToolUse hook events.
7
+ // Runs fast: read stdin → decide → append one line → exit.
8
+
9
+ 'use strict';
10
+
11
+ const fs = require('fs');
12
+ const path = require('path');
13
+ const os = require('os');
14
+
15
+ const cwd = process.cwd();
16
+ const planningDir = path.join(cwd, '.planning');
17
+ const activityFile = path.join(planningDir, 'activity.jsonl');
18
+
19
+ // Only write if .planning/ exists (i.e. this is a Declare project)
20
+ if (!fs.existsSync(planningDir)) process.exit(0);
21
+
22
+ let raw = '';
23
+ process.stdin.setEncoding('utf8');
24
+ process.stdin.on('data', c => raw += c);
25
+ process.stdin.on('end', () => {
26
+ try {
27
+ const data = JSON.parse(raw);
28
+ const event = buildEvent(data);
29
+ if (!event) process.exit(0);
30
+
31
+ // Ensure file exists
32
+ if (!fs.existsSync(activityFile)) fs.writeFileSync(activityFile, '');
33
+
34
+ // Append event + trim to last 200 lines
35
+ const line = JSON.stringify(event) + '\n';
36
+ fs.appendFileSync(activityFile, line);
37
+
38
+ // Trim to last 200 lines to avoid unbounded growth
39
+ const content = fs.readFileSync(activityFile, 'utf8');
40
+ const lines = content.split('\n').filter(Boolean);
41
+ if (lines.length > 200) {
42
+ fs.writeFileSync(activityFile, lines.slice(-200).join('\n') + '\n');
43
+ }
44
+ } catch (_) {
45
+ // Silent fail — never block Claude
46
+ }
47
+ process.exit(0);
48
+ });
49
+
50
+ /**
51
+ * Build an activity event from a hook payload, or return null to skip.
52
+ * @param {any} data
53
+ * @returns {object|null}
54
+ */
55
+ function buildEvent(data) {
56
+ const tool = data.tool_name || '';
57
+ const input = data.tool_input || {};
58
+ const response = data.tool_response;
59
+ const hookEvent = data.hook_event_name || ''; // PreToolUse or PostToolUse
60
+ const ts = Date.now();
61
+ const phase = hookEvent === 'PostToolUse' ? 'done' : 'start';
62
+
63
+ // Task spawns — most important for agent visibility
64
+ if (tool === 'Task') {
65
+ return {
66
+ ts, phase, tool: 'Task',
67
+ desc: input.description || '',
68
+ agent: input.subagent_type || '',
69
+ // truncate prompt to avoid massive payloads
70
+ prompt: (input.prompt || '').slice(0, 200),
71
+ bg: hookEvent === 'PostToolUse',
72
+ };
73
+ }
74
+
75
+ // Bash commands that involve declare-tools (execution steps)
76
+ if (tool === 'Bash') {
77
+ const cmd = input.command || '';
78
+ if (cmd.includes('declare-tools') || cmd.includes('/declare:')) {
79
+ return { ts, phase, tool: 'Bash', cmd: cmd.slice(0, 200) };
80
+ }
81
+ return null; // skip noisy general bash
82
+ }
83
+
84
+ // Write tool — track planning file changes + auto-sync on SUMMARY.md writes
85
+ if (tool === 'Write' && hookEvent === 'PostToolUse') {
86
+ const fp = input.file_path || '';
87
+ if (fp.includes('.planning/')) {
88
+ // When an executor writes A-XX-SUMMARY.md, auto-run sync-status so
89
+ // PLAN.md and MILESTONES.md update immediately without manual intervention
90
+ if (fp.includes('-SUMMARY.md')) {
91
+ const { execSync } = require('child_process');
92
+ try {
93
+ execSync(`node "${path.join(cwd, '.claude', 'declare-tools.cjs')}" sync-status`, {
94
+ cwd,
95
+ timeout: 10000,
96
+ stdio: 'ignore',
97
+ });
98
+ } catch (_) { /* silent — never block Claude */ }
99
+ }
100
+ return { ts, phase: 'done', tool: 'Write', file: fp.replace(cwd, '.') };
101
+ }
102
+ return null;
103
+ }
104
+
105
+ return null;
106
+ }
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env node
2
+ // Check for Declare updates in background, write result to cache
3
+ // Called by SessionStart hook - runs once per session
4
+
5
+ const fs = require('fs');
6
+ const path = require('path');
7
+ const os = require('os');
8
+ const { spawn } = require('child_process');
9
+
10
+ const homeDir = os.homedir();
11
+ const cwd = process.cwd();
12
+ const cacheDir = path.join(homeDir, '.claude', 'cache');
13
+ const cacheFile = path.join(cacheDir, 'declare-update-check.json');
14
+
15
+ // VERSION file locations (check project first, then global)
16
+ const projectVersionFile = path.join(cwd, '.claude', 'declare', 'VERSION');
17
+ const globalVersionFile = path.join(homeDir, '.claude', 'declare', 'VERSION');
18
+
19
+ // Ensure cache directory exists
20
+ if (!fs.existsSync(cacheDir)) {
21
+ fs.mkdirSync(cacheDir, { recursive: true });
22
+ }
23
+
24
+ // Run check in background (spawn background process, windowsHide prevents console flash)
25
+ const child = spawn(process.execPath, ['-e', `
26
+ const fs = require('fs');
27
+ const { execSync } = require('child_process');
28
+
29
+ const cacheFile = ${JSON.stringify(cacheFile)};
30
+ const projectVersionFile = ${JSON.stringify(projectVersionFile)};
31
+ const globalVersionFile = ${JSON.stringify(globalVersionFile)};
32
+
33
+ // Check project directory first (local install), then global
34
+ let installed = '0.0.0';
35
+ try {
36
+ if (fs.existsSync(projectVersionFile)) {
37
+ installed = fs.readFileSync(projectVersionFile, 'utf8').trim();
38
+ } else if (fs.existsSync(globalVersionFile)) {
39
+ installed = fs.readFileSync(globalVersionFile, 'utf8').trim();
40
+ }
41
+ } catch (e) {}
42
+
43
+ let latest = null;
44
+ try {
45
+ latest = execSync('npm view declare-cc version', { encoding: 'utf8', timeout: 10000, windowsHide: true }).trim();
46
+ } catch (e) {}
47
+
48
+ const result = {
49
+ update_available: latest && installed !== latest,
50
+ installed,
51
+ latest: latest || 'unknown',
52
+ checked: Math.floor(Date.now() / 1000)
53
+ };
54
+
55
+ fs.writeFileSync(cacheFile, JSON.stringify(result));
56
+ `], {
57
+ stdio: 'ignore',
58
+ windowsHide: true,
59
+ detached: true // Required on Windows for proper process detachment
60
+ });
61
+
62
+ child.unref();
@@ -0,0 +1,91 @@
1
+ #!/usr/bin/env node
2
+ // Claude Code Statusline - Declare Edition
3
+ // Shows: model | current task | directory | context usage
4
+
5
+ const fs = require('fs');
6
+ const path = require('path');
7
+ const os = require('os');
8
+
9
+ // Read JSON from stdin
10
+ let input = '';
11
+ process.stdin.setEncoding('utf8');
12
+ process.stdin.on('data', chunk => input += chunk);
13
+ process.stdin.on('end', () => {
14
+ try {
15
+ const data = JSON.parse(input);
16
+ const model = data.model?.display_name || 'Claude';
17
+ const dir = data.workspace?.current_dir || process.cwd();
18
+ const session = data.session_id || '';
19
+ const remaining = data.context_window?.remaining_percentage;
20
+
21
+ // Context window display (shows USED percentage scaled to 80% limit)
22
+ // Claude Code enforces an 80% context limit, so we scale to show 100% at that point
23
+ let ctx = '';
24
+ if (remaining != null) {
25
+ const rem = Math.round(remaining);
26
+ const rawUsed = Math.max(0, Math.min(100, 100 - rem));
27
+ // Scale: 80% real usage = 100% displayed
28
+ const used = Math.min(100, Math.round((rawUsed / 80) * 100));
29
+
30
+ // Build progress bar (10 segments)
31
+ const filled = Math.floor(used / 10);
32
+ const bar = '█'.repeat(filled) + '░'.repeat(10 - filled);
33
+
34
+ // Color based on scaled usage (thresholds adjusted for new scale)
35
+ if (used < 63) { // ~50% real
36
+ ctx = ` \x1b[32m${bar} ${used}%\x1b[0m`;
37
+ } else if (used < 81) { // ~65% real
38
+ ctx = ` \x1b[33m${bar} ${used}%\x1b[0m`;
39
+ } else if (used < 95) { // ~76% real
40
+ ctx = ` \x1b[38;5;208m${bar} ${used}%\x1b[0m`;
41
+ } else {
42
+ ctx = ` \x1b[5;31m💀 ${bar} ${used}%\x1b[0m`;
43
+ }
44
+ }
45
+
46
+ // Current task from todos
47
+ let task = '';
48
+ const homeDir = os.homedir();
49
+ const todosDir = path.join(homeDir, '.claude', 'todos');
50
+ if (session && fs.existsSync(todosDir)) {
51
+ try {
52
+ const files = fs.readdirSync(todosDir)
53
+ .filter(f => f.startsWith(session) && f.includes('-agent-') && f.endsWith('.json'))
54
+ .map(f => ({ name: f, mtime: fs.statSync(path.join(todosDir, f)).mtime }))
55
+ .sort((a, b) => b.mtime - a.mtime);
56
+
57
+ if (files.length > 0) {
58
+ try {
59
+ const todos = JSON.parse(fs.readFileSync(path.join(todosDir, files[0].name), 'utf8'));
60
+ const inProgress = todos.find(t => t.status === 'in_progress');
61
+ if (inProgress) task = inProgress.activeForm || '';
62
+ } catch (e) {}
63
+ }
64
+ } catch (e) {
65
+ // Silently fail on file system errors - don't break statusline
66
+ }
67
+ }
68
+
69
+ // Declare update available?
70
+ let gsdUpdate = '';
71
+ const cacheFile = path.join(homeDir, '.claude', 'cache', 'declare-update-check.json');
72
+ if (fs.existsSync(cacheFile)) {
73
+ try {
74
+ const cache = JSON.parse(fs.readFileSync(cacheFile, 'utf8'));
75
+ if (cache.update_available) {
76
+ gsdUpdate = '\x1b[33m⬆ /declare:update\x1b[0m │ ';
77
+ }
78
+ } catch (e) {}
79
+ }
80
+
81
+ // Output
82
+ const dirname = path.basename(dir);
83
+ if (task) {
84
+ process.stdout.write(`${gsdUpdate}\x1b[2m${model}\x1b[0m │ \x1b[1m${task}\x1b[0m │ \x1b[2m${dirname}\x1b[0m${ctx}`);
85
+ } else {
86
+ process.stdout.write(`${gsdUpdate}\x1b[2m${model}\x1b[0m │ \x1b[2m${dirname}\x1b[0m${ctx}`);
87
+ }
88
+ } catch (e) {
89
+ // Silent fail - don't break statusline on parse errors
90
+ }
91
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "declare-cc",
3
- "version": "0.4.8",
3
+ "version": "0.5.2",
4
4
  "description": "A future-driven meta-prompting engine for agentic development, rooted in declared futures and causal graph structure.",
5
5
  "bin": {
6
6
  "declare-cc": "bin/install.js"
@@ -10,6 +10,7 @@
10
10
  "commands",
11
11
  "agents",
12
12
  "dist",
13
+ "hooks",
13
14
  "scripts",
14
15
  "workflows",
15
16
  "templates"