diff --git a/scripts/session-closedown.js b/scripts/session-closedown.js index ec8bde8f..29a91a59 100755 --- a/scripts/session-closedown.js +++ b/scripts/session-closedown.js @@ -668,6 +668,64 @@ async function interactiveDeployment(gitAnalysis, autoAnswer = null) { return deploymentResult; } +/** + * Helper: Get scope adjustment summary (inst_052) + */ +function getScopeAdjustmentSummary() { + const scopeLogPath = path.join(__dirname, '../.claude/scope-adjustments.json'); + + if (!fs.existsSync(scopeLogPath)) { + return '✅ No scope adjustments made this session'; + } + + const log = JSON.parse(fs.readFileSync(scopeLogPath, 'utf8')); + const sessionState = JSON.parse(fs.readFileSync(SESSION_STATE_PATH, 'utf8')); + const sessionAdjustments = log.adjustments.filter(a => a.sessionId === sessionState.sessionId); + + if (sessionAdjustments.length === 0) { + return '✅ No scope adjustments made this session'; + } + + let summary = `⚠️ **${sessionAdjustments.length} scope adjustment(s) made:**\n\n`; + sessionAdjustments.forEach((adj, idx) => { + summary += `${idx + 1}. **${adj.type.toUpperCase()}** (message #${adj.messageNumber})\n`; + summary += ` - Rationale: ${adj.rationale}\n`; + if (adj.details) { + summary += ` - Details: ${adj.details}\n`; + } + summary += ` - User discretion: ${adj.userGrantedDiscretion ? 'Yes' : 'No'}\n\n`; + }); + + return summary; +} + +/** + * Helper: Get hook approval summary (inst_061) + */ +function getHookApprovalSummary() { + const approvalCachePath = path.join(__dirname, '../.claude/approval-cache.json'); + + if (!fs.existsSync(approvalCachePath)) { + return '✅ No hook approvals cached'; + } + + const cache = JSON.parse(fs.readFileSync(approvalCachePath, 'utf8')); + const sessionState = JSON.parse(fs.readFileSync(SESSION_STATE_PATH, 'utf8')); + + if (cache.sessionId !== sessionState.sessionId || cache.approvals.length === 0) { + return '✅ No hook approvals cached for this session'; + } + + let summary = `📋 **${cache.approvals.length} approval(s) cached:**\n\n`; + summary += '_These "don\'t ask again" approvals will be reset at next session start._\n\n'; + + cache.approvals.forEach((approval, idx) => { + summary += `${idx + 1}. \`${approval.commandType}\` in \`${approval.directory}\`\n`; + }); + + return summary + '\n'; +} + /** * Phase 5: Create Handoff Document (AT END with all results) */ @@ -801,6 +859,18 @@ ${gitAnalysis.recent_commits.join('\n')} --- +## Session Activity Tracking + +### Scope Adjustments (inst_052) + +${getScopeAdjustmentSummary()} + +### Hook Approvals (inst_061) + +${getHookApprovalSummary()} + +--- + ## Next Session **Startup Sequence**: diff --git a/scripts/session-init.js b/scripts/session-init.js index 50dbe89f..fca07a9d 100755 --- a/scripts/session-init.js +++ b/scripts/session-init.js @@ -371,6 +371,30 @@ async function main() { log(` Could not check background processes: ${err.message}`, 'yellow'); } + // Hook approval cache reset (inst_061) + section('2b. Hook Approval Cache Reset (inst_061)'); + try { + const approvalCache = '.claude/approval-cache.json'; + if (fs.existsSync(approvalCache)) { + const cache = JSON.parse(fs.readFileSync(approvalCache, 'utf8')); + const state = JSON.parse(fs.readFileSync('.claude/session-state.json', 'utf8')); + + if (cache.sessionId !== state.sessionId) { + // Session changed - reset cache + cache.sessionId = state.sessionId; + cache.approvals = []; + fs.writeFileSync(approvalCache, JSON.stringify(cache, null, 2)); + success('Hook approval cache reset for new session'); + } else { + log(` ${cache.approvals.length} approval(s) cached from current session`, 'cyan'); + } + } else { + success('No approval cache (will be created on first use)'); + } + } catch (err) { + log(` Could not check approval cache: ${err.message}`, 'yellow'); + } + // Load instruction history section('3. Loading Instruction History'); const instructions = loadInstructionHistory(); @@ -685,6 +709,48 @@ async function main() { warning(`Could not run CSP scan: ${err.message}`); } + // Defense-in-Depth Health Check (inst_072) + section('8a. Defense-in-Depth Health Check (inst_072)'); + try { + const auditResult = execSync('node scripts/audit-defense-in-depth.js 2>&1', { + encoding: 'utf8', + stdio: 'pipe' + }); + + if (auditResult.includes('All 5 layers')) { + success('All 5 credential protection layers verified'); + } else { + const incompleteCount = (auditResult.match(/❌/g) || []).length; + warning(`${incompleteCount} defense layer(s) incomplete`); + log(' Run: node scripts/audit-defense-in-depth.js for details', 'cyan'); + } + } catch (err) { + // Non-blocking - just warn + warning('Defense-in-depth audit incomplete (see details above)'); + } + + // Dependency License Check (inst_080) + section('8b. Dependency License Check (inst_080)'); + try { + execSync('node scripts/check-dependency-licenses.js', { + encoding: 'utf8', + stdio: 'pipe' + }); + success('All dependencies are Apache 2.0 compatible'); + } catch (err) { + // Check if it's a critical failure or just warnings + const output = err.stdout || ''; + if (output.includes('CRITICAL')) { + error('Prohibited dependency licenses detected'); + log(' Run: node scripts/check-dependency-licenses.js for details', 'red'); + } else if (output.includes('HIGH')) { + warning('Restrictive licenses detected (may require review)'); + log(' Run: node scripts/check-dependency-licenses.js for details', 'yellow'); + } else { + success('Dependency licenses checked (some flagged for review)'); + } + } + // ENFORCEMENT: Local development server check section('9. Development Environment Enforcement'); const localServerRunning = checkLocalServer();