UNPKG

aiwg

Version:

Deployment tool and support utility for AI context. Copies agents, skills, commands, rules, and behaviors into the paths each AI platform reads (Claude Code, Codex, Copilot, Cursor, Warp, OpenClaw, and 6 more) so one source of truth works across 10 platfo

114 lines 4.25 kB
/** * Project-Local Activity Log Helper * * Inline activity-log emission for project-local artifact lifecycle events. * Wraps the storage adapter from `resolveStorage('activity_log')` and emits * single-line entries per the format frozen by the activity-log rule. * * The generic post-command hook in `cli/hooks/builtin/activity-log-hook.ts` * emits one entry per CLI invocation (`aiwg use sdlc`). This helper emits * per-event entries (per-bundle, per-provider) at finer granularity, as * specified by the design doc. * * @design @.aiwg/architecture/design-doctor-log-promote.md * @implements #1037 * @issue #1049 */ import { formatEntry } from '../activity-log/types.js'; import { resolveStorage } from '../storage/index.js'; const LOG_PATH = 'activity.log'; /** Map design-event → frozen wire-format op token. */ const EVENT_TO_OP = { discover: 'query', deploy: 'deploy', 'deploy-failed': 'deploy', conflict: 'deploy', 'shadow-acknowledged': 'deploy', 'shadow-refused': 'deploy', remove: 'delete', 'remove-mutated': 'delete', 'remove-conflict': 'delete', 'remove-force': 'delete', promote: 'promote', 'promote-failed': 'promote', }; /** * Append an activity-log entry for a project-local lifecycle event. * * Non-blocking: write failures are swallowed with a stderr warning. The * caller's primary operation must not depend on log success. * * Wire format: `## [TS] <op> | <event>: <name>:<type> | <summary>` */ export async function appendProjectLocalActivity(ev) { if (process.env['AIWG_SKIP_ACTIVITY_LOG']) { const v = process.env['AIWG_SKIP_ACTIVITY_LOG'].toLowerCase().trim(); if (v && v !== '0' && v !== 'false') return; } const op = EVENT_TO_OP[ev.event]; const compactSummary = `${ev.event}: ${ev.name}:${ev.type} | ${ev.summary}`; const line = formatEntry({ timestamp: new Date(), operation: op, summary: compactSummary, }) + '\n'; try { const adapter = await resolveStorage('activity_log'); if (typeof adapter.append === 'function') { const existing = (await adapter.read(LOG_PATH)) ?? ''; if (existing.length > 0 && !existing.endsWith('\n')) { await adapter.append(LOG_PATH, '\n'); } await adapter.append(LOG_PATH, line); } else { const existing = (await adapter.read(LOG_PATH)) ?? ''; const trailing = existing.length === 0 || existing.endsWith('\n') ? '' : '\n'; await adapter.write(LOG_PATH, existing + trailing + line); } } catch (err) { const msg = err instanceof Error ? err.message : String(err); process.stderr.write(`project-local activity log append failed (non-fatal): ${msg}\n`); } } /** * Emit `discover` events for newly-seen bundles. Per #1049 design's * "discover noise control": dedupe by reading the most recent N lines * and skipping (name, type) pairs that already appeared. * * Cheap heuristic — exact log de-dup is not required. We just want to * avoid filling the log with the same lines on every read-only command. */ export async function emitDiscoverEventsDeduped(bundles) { if (bundles.length === 0) return; if (process.env['AIWG_SKIP_ACTIVITY_LOG']) { const v = process.env['AIWG_SKIP_ACTIVITY_LOG'].toLowerCase().trim(); if (v && v !== '0' && v !== 'false') return; } let recent = ''; try { const adapter = await resolveStorage('activity_log'); recent = (await adapter.read(LOG_PATH)) ?? ''; } catch { // Read failed — fall through and emit unconditionally (rather than // suppress important events because we couldn't read history). } const tail = recent.split('\n').slice(-200).join('\n'); for (const b of bundles) { const marker = `discover: ${b.id}:${b.type}`; if (tail.includes(marker)) continue; await appendProjectLocalActivity({ event: 'discover', name: b.id, type: b.type, summary: `manifest discovered`, }); } } //# sourceMappingURL=project-local-activity.js.map