declare-cc 0.5.0 → 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/dist/declare-tools.cjs +8 -1
- package/dist/public/app.js +113 -13
- package/dist/public/index.html +44 -0
- package/hooks/declare-activity.js +13 -1
- package/package.json +1 -1
package/dist/declare-tools.cjs
CHANGED
|
@@ -1329,7 +1329,7 @@ var require_help = __commonJS({
|
|
|
1329
1329
|
usage: "/declare:help"
|
|
1330
1330
|
}
|
|
1331
1331
|
],
|
|
1332
|
-
version: "0.
|
|
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;
|
package/dist/public/app.js
CHANGED
|
@@ -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 {
|
|
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(
|
|
215
|
-
el.dataset.nodeId
|
|
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">${
|
|
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
|
|
243
|
-
|
|
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
|
-
|
|
248
|
-
|
|
249
|
-
|
|
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
|
-
|
|
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();
|
package/dist/public/index.html
CHANGED
|
@@ -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);
|
|
@@ -81,10 +81,22 @@ function buildEvent(data) {
|
|
|
81
81
|
return null; // skip noisy general bash
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
// Write tool — track planning file changes
|
|
84
|
+
// Write tool — track planning file changes + auto-sync on SUMMARY.md writes
|
|
85
85
|
if (tool === 'Write' && hookEvent === 'PostToolUse') {
|
|
86
86
|
const fp = input.file_path || '';
|
|
87
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
|
+
}
|
|
88
100
|
return { ts, phase: 'done', tool: 'Write', file: fp.replace(cwd, '.') };
|
|
89
101
|
}
|
|
90
102
|
return null;
|
package/package.json
CHANGED