@codemieai/code 0.3.2 → 0.4.1

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.
Files changed (118) hide show
  1. package/README.md +2 -0
  2. package/dist/agents/core/session/BaseSessionAdapter.d.ts +5 -2
  3. package/dist/agents/core/session/BaseSessionAdapter.d.ts.map +1 -1
  4. package/dist/agents/core/types.d.ts +11 -0
  5. package/dist/agents/core/types.d.ts.map +1 -1
  6. package/dist/agents/plugins/claude/claude.plugin.js +3 -3
  7. package/dist/agents/plugins/claude/claude.session.d.ts +13 -0
  8. package/dist/agents/plugins/claude/claude.session.d.ts.map +1 -1
  9. package/dist/agents/plugins/claude/claude.session.js +122 -42
  10. package/dist/agents/plugins/claude/claude.session.js.map +1 -1
  11. package/dist/agents/plugins/claude/plugin/.claude-plugin/plugin.json +1 -1
  12. package/dist/agents/plugins/claude/plugin/skills/codemie-analytics/scripts/analytics-cli.js +12 -8
  13. package/dist/agents/plugins/claude/plugin/skills/msgraph/SKILL.md +95 -8
  14. package/dist/agents/plugins/claude/plugin/skills/msgraph/scripts/msgraph.js +333 -2
  15. package/dist/agents/plugins/claude/plugin/statusline.mjs +11 -3
  16. package/dist/agents/plugins/claude/session/claude-file-operation.d.ts +38 -0
  17. package/dist/agents/plugins/claude/session/claude-file-operation.d.ts.map +1 -0
  18. package/dist/agents/plugins/claude/session/claude-file-operation.js +67 -0
  19. package/dist/agents/plugins/claude/session/claude-file-operation.js.map +1 -0
  20. package/dist/agents/plugins/claude/session/processors/claude.metrics-processor.d.ts +0 -4
  21. package/dist/agents/plugins/claude/session/processors/claude.metrics-processor.d.ts.map +1 -1
  22. package/dist/agents/plugins/claude/session/processors/claude.metrics-processor.js +7 -56
  23. package/dist/agents/plugins/claude/session/processors/claude.metrics-processor.js.map +1 -1
  24. package/dist/bin/proxy-daemon.js +54 -5
  25. package/dist/bin/proxy-daemon.js.map +1 -1
  26. package/dist/cli/commands/analytics/aggregator.d.ts +1 -1
  27. package/dist/cli/commands/analytics/aggregator.d.ts.map +1 -1
  28. package/dist/cli/commands/analytics/aggregator.js +84 -23
  29. package/dist/cli/commands/analytics/aggregator.js.map +1 -1
  30. package/dist/cli/commands/analytics/cost/cost-calculator.d.ts +10 -0
  31. package/dist/cli/commands/analytics/cost/cost-calculator.d.ts.map +1 -0
  32. package/dist/cli/commands/analytics/cost/cost-calculator.js +24 -0
  33. package/dist/cli/commands/analytics/cost/cost-calculator.js.map +1 -0
  34. package/dist/cli/commands/analytics/cost/cost-enricher.d.ts +24 -0
  35. package/dist/cli/commands/analytics/cost/cost-enricher.d.ts.map +1 -0
  36. package/dist/cli/commands/analytics/cost/cost-enricher.js +152 -0
  37. package/dist/cli/commands/analytics/cost/cost-enricher.js.map +1 -0
  38. package/dist/cli/commands/analytics/cost/pricing.d.ts +23 -0
  39. package/dist/cli/commands/analytics/cost/pricing.d.ts.map +1 -0
  40. package/dist/cli/commands/analytics/cost/pricing.js +82 -0
  41. package/dist/cli/commands/analytics/cost/pricing.js.map +1 -0
  42. package/dist/cli/commands/analytics/cost/pricing.json +975 -0
  43. package/dist/cli/commands/analytics/cost/types.d.ts +51 -0
  44. package/dist/cli/commands/analytics/cost/types.d.ts.map +1 -0
  45. package/dist/cli/commands/analytics/cost/types.js +8 -0
  46. package/dist/cli/commands/analytics/cost/types.js.map +1 -0
  47. package/dist/cli/commands/analytics/cost/usage-readers.d.ts +40 -0
  48. package/dist/cli/commands/analytics/cost/usage-readers.d.ts.map +1 -0
  49. package/dist/cli/commands/analytics/cost/usage-readers.js +165 -0
  50. package/dist/cli/commands/analytics/cost/usage-readers.js.map +1 -0
  51. package/dist/cli/commands/analytics/data-loader.d.ts +11 -0
  52. package/dist/cli/commands/analytics/data-loader.d.ts.map +1 -1
  53. package/dist/cli/commands/analytics/data-loader.js +7 -0
  54. package/dist/cli/commands/analytics/data-loader.js.map +1 -1
  55. package/dist/cli/commands/analytics/exporter.d.ts.map +1 -1
  56. package/dist/cli/commands/analytics/exporter.js +2 -2
  57. package/dist/cli/commands/analytics/exporter.js.map +1 -1
  58. package/dist/cli/commands/analytics/index.d.ts.map +1 -1
  59. package/dist/cli/commands/analytics/index.js +107 -10
  60. package/dist/cli/commands/analytics/index.js.map +1 -1
  61. package/dist/cli/commands/analytics/native-loader.d.ts +48 -0
  62. package/dist/cli/commands/analytics/native-loader.d.ts.map +1 -0
  63. package/dist/cli/commands/analytics/native-loader.js +220 -0
  64. package/dist/cli/commands/analytics/native-loader.js.map +1 -0
  65. package/dist/cli/commands/analytics/report/assets/chart.umd.js +14 -0
  66. package/dist/cli/commands/analytics/report/assets/codemie-bundle.css +1 -0
  67. package/dist/cli/commands/analytics/report/client/app.js +676 -0
  68. package/dist/cli/commands/analytics/report/payload-builder.d.ts +15 -0
  69. package/dist/cli/commands/analytics/report/payload-builder.d.ts.map +1 -0
  70. package/dist/cli/commands/analytics/report/payload-builder.js +107 -0
  71. package/dist/cli/commands/analytics/report/payload-builder.js.map +1 -0
  72. package/dist/cli/commands/analytics/report/report-generator.d.ts +47 -0
  73. package/dist/cli/commands/analytics/report/report-generator.d.ts.map +1 -0
  74. package/dist/cli/commands/analytics/report/report-generator.js +102 -0
  75. package/dist/cli/commands/analytics/report/report-generator.js.map +1 -0
  76. package/dist/cli/commands/analytics/report/template.html +217 -0
  77. package/dist/cli/commands/analytics/report/types.d.ts +55 -0
  78. package/dist/cli/commands/analytics/report/types.d.ts.map +1 -0
  79. package/dist/cli/commands/analytics/report/types.js +6 -0
  80. package/dist/cli/commands/analytics/report/types.js.map +1 -0
  81. package/dist/cli/commands/analytics/types.d.ts +13 -0
  82. package/dist/cli/commands/analytics/types.d.ts.map +1 -1
  83. package/dist/cli/commands/proxy/daemon-manager.d.ts +5 -0
  84. package/dist/cli/commands/proxy/daemon-manager.d.ts.map +1 -1
  85. package/dist/cli/commands/proxy/daemon-manager.js +25 -9
  86. package/dist/cli/commands/proxy/daemon-manager.js.map +1 -1
  87. package/dist/cli/commands/proxy/health-check.d.ts +16 -0
  88. package/dist/cli/commands/proxy/health-check.d.ts.map +1 -0
  89. package/dist/cli/commands/proxy/health-check.js +81 -0
  90. package/dist/cli/commands/proxy/health-check.js.map +1 -0
  91. package/dist/cli/commands/proxy/index.d.ts.map +1 -1
  92. package/dist/cli/commands/proxy/index.js +54 -4
  93. package/dist/cli/commands/proxy/index.js.map +1 -1
  94. package/dist/cli/commands/proxy/watcher.d.ts +31 -0
  95. package/dist/cli/commands/proxy/watcher.d.ts.map +1 -0
  96. package/dist/cli/commands/proxy/watcher.js +97 -0
  97. package/dist/cli/commands/proxy/watcher.js.map +1 -0
  98. package/dist/cli/commands/skills/setup/sync-plugin.d.ts +15 -0
  99. package/dist/cli/commands/skills/setup/sync-plugin.d.ts.map +1 -0
  100. package/dist/cli/commands/skills/setup/sync-plugin.js +63 -0
  101. package/dist/cli/commands/skills/setup/sync-plugin.js.map +1 -0
  102. package/dist/providers/plugins/sso/proxy/proxy-errors.js +2 -2
  103. package/dist/providers/plugins/sso/proxy/proxy-errors.js.map +1 -1
  104. package/dist/providers/plugins/sso/proxy/proxy-types.d.ts +6 -0
  105. package/dist/providers/plugins/sso/proxy/proxy-types.d.ts.map +1 -1
  106. package/dist/providers/plugins/sso/proxy/sso.proxy.d.ts +1 -0
  107. package/dist/providers/plugins/sso/proxy/sso.proxy.d.ts.map +1 -1
  108. package/dist/providers/plugins/sso/proxy/sso.proxy.js +39 -3
  109. package/dist/providers/plugins/sso/proxy/sso.proxy.js.map +1 -1
  110. package/dist/providers/plugins/sso/session/processors/metrics/metrics-aggregator.js +14 -4
  111. package/dist/providers/plugins/sso/session/processors/metrics/metrics-aggregator.js.map +1 -1
  112. package/dist/providers/plugins/sso/session/processors/metrics/metrics-post-processor.d.ts.map +1 -1
  113. package/dist/providers/plugins/sso/session/processors/metrics/metrics-post-processor.js +5 -0
  114. package/dist/providers/plugins/sso/session/processors/metrics/metrics-post-processor.js.map +1 -1
  115. package/dist/providers/plugins/sso/session/processors/metrics/metrics-types.d.ts +2 -0
  116. package/dist/providers/plugins/sso/session/processors/metrics/metrics-types.d.ts.map +1 -1
  117. package/package.json +1 -1
  118. package/scripts/copy-plugins.js +39 -0
@@ -44,10 +44,12 @@ const SCOPES = [
44
44
  'ChannelMessage.Read.All', 'ChannelMessage.Send',
45
45
  'OnlineMeetingTranscript.Read.All', 'OnlineMeetings.Read',
46
46
  'People.Read', 'Contacts.Read', 'offline_access',
47
- 'Notes.Read', 'Notes.ReadWrite',
47
+ 'Notes.Read', 'Notes.ReadWrite', 'OnlineMeetingAiInsight.Read.All', 'Tasks.ReadWrite',
48
+ 'Group.Read.All',
48
49
  ].join(' ');
49
50
  const CACHE_FILE = path.join(os.homedir(), '.ms_graph_token_cache.json');
50
51
  const GRAPH_BASE = 'https://graph.microsoft.com/v1.0';
52
+ const GRAPH_BETA = 'https://graph.microsoft.com/beta';
51
53
  const TIMEOUT_MS = 5 * 60 * 1000;
52
54
 
53
55
  // ── HTTP Helpers ──────────────────────────────────────────────────────────────
@@ -112,6 +114,21 @@ async function graphPost(endpoint, token, body) {
112
114
  return res.body ? JSON.parse(res.body) : {};
113
115
  }
114
116
 
117
+ async function graphPatch(endpoint, token, body, extraHeaders = {}) {
118
+ const bodyStr = JSON.stringify(body);
119
+ const res = await httpsRequest(`${GRAPH_BASE}${endpoint}`, {
120
+ method: 'PATCH',
121
+ headers: {
122
+ Authorization: `Bearer ${token}`,
123
+ 'Content-Type': 'application/json',
124
+ 'Content-Length': Buffer.byteLength(bodyStr),
125
+ ...extraHeaders,
126
+ },
127
+ }, bodyStr);
128
+ // Planner PATCH returns 204 with an empty body unless Prefer: return=representation is sent.
129
+ return res.body ? JSON.parse(res.body) : {};
130
+ }
131
+
115
132
  function graphDownload(endpoint, token) {
116
133
  function fetch(url, auth) {
117
134
  return new Promise((resolve, reject) => {
@@ -342,6 +359,15 @@ function fmtDt(iso) {
342
359
  catch { return (iso || '').slice(0, 16).replace('T', ' '); }
343
360
  }
344
361
 
362
+ // Formats a Graph dateTimeTimeZone object ({ dateTime, timeZone }). To Do returns a
363
+ // zone-less dateTime, so a UTC value would otherwise be parsed as local time by fmtDt.
364
+ function fmtDtTz(dtz) {
365
+ if (!dtz?.dateTime) return 'N/A';
366
+ let s = dtz.dateTime;
367
+ if (!/[zZ]|[+-]\d{2}:\d{2}$/.test(s) && (dtz.timeZone || '').toUpperCase() === 'UTC') s += 'Z';
368
+ return fmtDt(s);
369
+ }
370
+
345
371
  function fmtSize(n) {
346
372
  if (!n) return '';
347
373
  const units = ['B', 'KB', 'MB', 'GB', 'TB'];
@@ -750,9 +776,135 @@ async function cmdChannels(args) {
750
776
  console.log(' --team-id ID --channel-id ID --send MSG');
751
777
  }
752
778
 
779
+ // ── Meeting AI Insights (Copilot recap) ────────────────────────────────────────
780
+ // Lives in the Microsoft 365 Copilot API namespace and is keyed by user object ID
781
+ // (not /me). The aiInsights navigation is documented under beta, so try v1.0 first
782
+ // and fall back to beta on 404. Requires a Microsoft 365 Copilot license.
783
+ async function getAiInsights(token, oid, meetingId, insightId) {
784
+ const suffix = insightId ? `/${insightId}` : '';
785
+ const ep = `/copilot/users/${oid}/onlineMeetings/${meetingId}/aiInsights${suffix}`;
786
+ const headers = { Authorization: `Bearer ${token}` };
787
+ try {
788
+ const res = await httpsRequest(`${GRAPH_BASE}${ep}`, { headers });
789
+ return JSON.parse(res.body);
790
+ } catch (err) {
791
+ if (err.statusCode === 404) {
792
+ const res = await httpsRequest(`${GRAPH_BETA}${ep}`, { headers });
793
+ return JSON.parse(res.body);
794
+ }
795
+ throw err;
796
+ }
797
+ }
798
+
799
+ async function printMeetingInsights(token, oid, meetingId, args) {
800
+ let list;
801
+ try {
802
+ list = await getAiInsights(token, oid, meetingId);
803
+ } catch (err) {
804
+ if (err.statusCode === 403) {
805
+ console.error('AI insights require a Microsoft 365 Copilot license (and transcription/recording enabled for the meeting).');
806
+ return;
807
+ }
808
+ if (err.statusCode === 404) {
809
+ console.log('No AI insights available for this meeting (none generated yet, or unsupported meeting type).');
810
+ return;
811
+ }
812
+ throw err;
813
+ }
814
+
815
+ const insights = list.value || [];
816
+ if (!insights.length) {
817
+ console.log('No AI insights yet. Insights generate after the meeting ends and can take up to 4 hours.');
818
+ return;
819
+ }
820
+
821
+ const full = [];
822
+ for (const ins of insights) {
823
+ try { full.push(await getAiInsights(token, oid, meetingId, ins.id)); }
824
+ catch { full.push(ins); }
825
+ }
826
+
827
+ if (args.json) { console.log(JSON.stringify(full, null, 2)); return; }
828
+
829
+ for (const ai of full) {
830
+ console.log(`\n${'═'.repeat(60)}`);
831
+ console.log(`AI Insight ${ai.id || ''} (${fmtDt(ai.createdDateTime)})`);
832
+
833
+ const notes = ai.meetingNotes || [];
834
+ if (notes.length) {
835
+ console.log('\nMeeting Notes:');
836
+ for (const n of notes) {
837
+ console.log(` • ${n.title || ''}`);
838
+ if (n.text) console.log(` ${n.text}`);
839
+ for (const sp of n.subpoints || []) {
840
+ console.log(` – ${sp.title || ''}`);
841
+ if (sp.text) console.log(` ${sp.text}`);
842
+ }
843
+ }
844
+ }
845
+
846
+ const items = ai.actionItems || [];
847
+ if (items.length) {
848
+ console.log('\nAction Items:');
849
+ for (const it of items)
850
+ console.log(` ☐ ${it.title || ''}${it.ownerDisplayName ? ` (owner: ${it.ownerDisplayName})` : ''}`);
851
+ }
852
+
853
+ const mentions = ai.viewpoint?.mentionEvents || [];
854
+ if (mentions.length) {
855
+ console.log('\nMentions:');
856
+ for (const m of mentions)
857
+ console.log(` @ ${fmtDt(m.eventDateTime)} ${m.speaker?.user?.displayName || ''}: ${(m.transcriptUtterance || '').slice(0, 200)}`);
858
+ }
859
+ }
860
+ }
861
+
862
+ async function cmdTranscriptsInsights(args, token) {
863
+ const me = await graphGet('/me', token, { $select: 'id' });
864
+ const oid = me.id;
865
+
866
+ // Direct meeting ID short-circuits resolution.
867
+ if (args.meeting) {
868
+ await printMeetingInsights(token, oid, args.meeting, args);
869
+ return;
870
+ }
871
+
872
+ // Otherwise resolve online meetings via the calendar (same approach as --subject).
873
+ const startDate = args.start || new Date(Date.now() - 7 * 86400 * 1000).toISOString().slice(0, 10);
874
+ const endDate = args.end || startDate;
875
+ const data = await graphGet('/me/calendarView', token, {
876
+ startDateTime: startDate + 'T00:00:00Z',
877
+ endDateTime: endDate + 'T23:59:59Z',
878
+ $select: 'subject,start,isOnlineMeeting,onlineMeeting',
879
+ $top: 50,
880
+ $orderby: 'start/dateTime',
881
+ });
882
+ let events = (data.value || []).filter(e => e.isOnlineMeeting && e.onlineMeeting?.joinUrl);
883
+ if (args.subject) {
884
+ const kw = args.subject.toLowerCase();
885
+ events = events.filter(e => (e.subject || '').toLowerCase().includes(kw));
886
+ }
887
+ if (!events.length) { console.log('No matching online meetings found in range.'); return; }
888
+
889
+ for (const e of events) {
890
+ let meetingId = null;
891
+ try {
892
+ const om = await graphGet('/me/onlineMeetings', token, { $filter: `joinWebUrl eq '${e.onlineMeeting.joinUrl}'` });
893
+ meetingId = (om.value || [])[0]?.id || null;
894
+ } catch (err) {
895
+ console.log(`Could not resolve meeting ID for "${e.subject}": ${err.message}`);
896
+ }
897
+ if (!meetingId) continue;
898
+ console.log(`\nMeeting: ${e.subject} (${fmtDt(e.start?.dateTime)}) — ${meetingId}`);
899
+ await printMeetingInsights(token, oid, meetingId, args);
900
+ }
901
+ }
902
+
753
903
  async function cmdTranscripts(args) {
754
904
  const token = await getValidToken();
755
905
 
906
+ if (args.insights) return cmdTranscriptsInsights(args, token);
907
+
756
908
  if (args.list || (!args.meeting && !args.download)) {
757
909
  const startDate = args.start || new Date(Date.now() - 7 * 86400 * 1000).toISOString().slice(0, 10);
758
910
  const endDate = args.end || startDate;
@@ -1161,10 +1313,179 @@ async function cmdOnenote(args) {
1161
1313
  console.log(' --create TITLE --section SECTION_ID [--body CONTENT]');
1162
1314
  }
1163
1315
 
1316
+ async function cmdPlanner(args) {
1317
+ const token = await getValidToken();
1318
+ const limit = parseInt(args.limit) || 20;
1319
+
1320
+ // Plans shared with me (single call; plans live in M365 groups → needs Group.Read.All).
1321
+ // Planner rejects OData params ($top/$select/$filter return HTTP 400) — fetch all, slice client-side.
1322
+ if (args.plans) {
1323
+ const data = await graphGet('/me/planner/plans', token);
1324
+ const plans = (data.value || []).slice(0, limit);
1325
+ if (args.json) { console.log(JSON.stringify(plans, null, 2)); return; }
1326
+ if (!plans.length) { console.log('No plans found.'); return; }
1327
+ console.log(`\n${'Plan ID'.padEnd(30)} Title`);
1328
+ console.log('─'.repeat(70));
1329
+ for (const p of plans)
1330
+ console.log(`${(p.id || '').padEnd(30)} ${p.title || '(untitled)'}`);
1331
+ return;
1332
+ }
1333
+
1334
+ // Tasks assigned to me (returns planId/bucketId only — no names). No OData params on Planner.
1335
+ if (args.myTasks) {
1336
+ const data = await graphGet('/me/planner/tasks', token);
1337
+ const tasks = (data.value || []).slice(0, limit);
1338
+ if (args.json) { console.log(JSON.stringify(tasks, null, 2)); return; }
1339
+ if (!tasks.length) { console.log('No tasks assigned to you.'); return; }
1340
+ console.log(`\n${'Task ID'.padEnd(30)} ${'%'.padEnd(4)} ${'Due'.padEnd(16)} Title`);
1341
+ console.log('─'.repeat(80));
1342
+ for (const t of tasks) {
1343
+ const due = t.dueDateTime ? fmtDt(t.dueDateTime) : '';
1344
+ console.log(`${(t.id || '').padEnd(30)} ${String(t.percentComplete ?? 0).padEnd(4)} ${pad(due, 16)} ${t.title || '(untitled)'}`);
1345
+ }
1346
+ return;
1347
+ }
1348
+
1349
+ // Buckets (columns) in a plan.
1350
+ if (args.planId && args.buckets) {
1351
+ const data = await graphGet(`/planner/plans/${args.planId}/buckets`, token);
1352
+ const buckets = data.value || [];
1353
+ if (args.json) { console.log(JSON.stringify(buckets, null, 2)); return; }
1354
+ if (!buckets.length) { console.log('No buckets found.'); return; }
1355
+ console.log(`\n${'Bucket ID'.padEnd(30)} Name`);
1356
+ console.log('─'.repeat(70));
1357
+ for (const b of buckets)
1358
+ console.log(`${(b.id || '').padEnd(30)} ${b.name || '(unnamed)'}`);
1359
+ return;
1360
+ }
1361
+
1362
+ // Create a task in a plan.
1363
+ if (args.planId && typeof args.create === 'string') {
1364
+ const payload = { planId: args.planId, title: args.create };
1365
+ if (args.bucketId) payload.bucketId = args.bucketId;
1366
+ if (args.due) payload.dueDateTime = `${args.due}T00:00:00Z`;
1367
+ if (args.assign) {
1368
+ payload.assignments = {
1369
+ [args.assign]: { '@odata.type': '#microsoft.graph.plannerAssignment', orderHint: ' !' },
1370
+ };
1371
+ }
1372
+ const task = await graphPost('/planner/tasks', token, payload);
1373
+ console.log(`Task created: ${task.title || args.create}`);
1374
+ console.log(`ID: ${task.id}`);
1375
+ return;
1376
+ }
1377
+
1378
+ // Tasks in a plan. No OData params on Planner.
1379
+ if (args.planId && args.tasks) {
1380
+ const data = await graphGet(`/planner/plans/${args.planId}/tasks`, token);
1381
+ const tasks = (data.value || []).slice(0, limit);
1382
+ if (args.json) { console.log(JSON.stringify(tasks, null, 2)); return; }
1383
+ if (!tasks.length) { console.log('No tasks in this plan.'); return; }
1384
+ console.log(`\n${'Task ID'.padEnd(30)} ${'%'.padEnd(4)} ${'Due'.padEnd(16)} Title`);
1385
+ console.log('─'.repeat(80));
1386
+ for (const t of tasks) {
1387
+ const due = t.dueDateTime ? fmtDt(t.dueDateTime) : '';
1388
+ console.log(`${(t.id || '').padEnd(30)} ${String(t.percentComplete ?? 0).padEnd(4)} ${pad(due, 16)} ${t.title || '(untitled)'}`);
1389
+ }
1390
+ return;
1391
+ }
1392
+
1393
+ // Mark a task complete: read its etag, then PATCH percentComplete to 100.
1394
+ if (typeof args.complete === 'string') {
1395
+ const task = await graphGet(`/planner/tasks/${args.complete}`, token);
1396
+ const etag = task['@odata.etag'];
1397
+ if (!etag) { console.error('Could not read the task etag; cannot update.'); process.exit(1); }
1398
+ await graphPatch(`/planner/tasks/${args.complete}`, token, { percentComplete: 100 }, { 'If-Match': etag });
1399
+ console.log(`Task marked complete: ${task.title || args.complete}`);
1400
+ return;
1401
+ }
1402
+
1403
+ // Single task detail (+ description/checklist from /details).
1404
+ if (args.taskId) {
1405
+ const task = await graphGet(`/planner/tasks/${args.taskId}`, token);
1406
+ let details = {};
1407
+ try { details = await graphGet(`/planner/tasks/${args.taskId}/details`, token); } catch {}
1408
+ if (args.json) { console.log(JSON.stringify({ ...task, details }, null, 2)); return; }
1409
+ console.log(`Title : ${task.title || '(untitled)'}`);
1410
+ console.log(`% Complete : ${task.percentComplete ?? 0}`);
1411
+ console.log(`Due : ${task.dueDateTime ? fmtDt(task.dueDateTime) : 'N/A'}`);
1412
+ console.log(`Bucket ID : ${task.bucketId || 'N/A'}`);
1413
+ console.log(`Plan ID : ${task.planId || 'N/A'}`);
1414
+ if (details.description) console.log(`\nDescription:\n${details.description}`);
1415
+ return;
1416
+ }
1417
+
1418
+ console.log('Planner: --plans | --my-tasks');
1419
+ console.log(' --plan-id ID --tasks | --plan-id ID --buckets');
1420
+ console.log(' --plan-id ID --create "TITLE" [--bucket-id ID] [--due YYYY-MM-DD] [--assign USER_ID]');
1421
+ console.log(' --task-id ID | --complete TASK_ID');
1422
+ }
1423
+
1424
+ async function cmdTodo(args) {
1425
+ const token = await getValidToken();
1426
+ const limit = parseInt(args.limit) || 20;
1427
+
1428
+ if (args.lists) {
1429
+ const data = await graphGet('/me/todo/lists', token, { $top: limit });
1430
+ const lists = data.value || [];
1431
+ if (args.json) { console.log(JSON.stringify(lists, null, 2)); return; }
1432
+ if (!lists.length) { console.log('No task lists found.'); return; }
1433
+ console.log(`\n${'List ID'.padEnd(50)} Name`);
1434
+ console.log('─'.repeat(80));
1435
+ for (const l of lists)
1436
+ console.log(`${(l.id || '').padEnd(50)} ${l.displayName || '(unnamed)'}`);
1437
+ return;
1438
+ }
1439
+
1440
+ if (!args.listId) {
1441
+ console.log('To Do: --lists');
1442
+ console.log(' --list-id ID --tasks');
1443
+ console.log(' --list-id ID --create "TITLE" [--body TEXT] [--due YYYY-MM-DD]');
1444
+ console.log(' --list-id ID --complete TASK_ID');
1445
+ return;
1446
+ }
1447
+
1448
+ // Create a task in a list.
1449
+ if (typeof args.create === 'string') {
1450
+ const payload = { title: args.create };
1451
+ if (args.body) payload.body = { content: args.body, contentType: 'text' };
1452
+ if (args.due) payload.dueDateTime = { dateTime: `${args.due}T00:00:00.000000`, timeZone: 'UTC' };
1453
+ const task = await graphPost(`/me/todo/lists/${args.listId}/tasks`, token, payload);
1454
+ console.log(`Task created: ${task.title || args.create}`);
1455
+ console.log(`ID: ${task.id}`);
1456
+ return;
1457
+ }
1458
+
1459
+ // Mark a task complete (To Do needs no etag).
1460
+ if (typeof args.complete === 'string') {
1461
+ await graphPatch(`/me/todo/lists/${args.listId}/tasks/${args.complete}`, token, { status: 'completed' });
1462
+ console.log(`Task marked complete: ${args.complete}`);
1463
+ return;
1464
+ }
1465
+
1466
+ // Tasks in a list.
1467
+ if (args.tasks) {
1468
+ const data = await graphGet(`/me/todo/lists/${args.listId}/tasks`, token, { $top: limit });
1469
+ const tasks = data.value || [];
1470
+ if (args.json) { console.log(JSON.stringify(tasks, null, 2)); return; }
1471
+ if (!tasks.length) { console.log('No tasks in this list.'); return; }
1472
+ console.log(`\n${'Task ID'.padEnd(50)} ${'Status'.padEnd(12)} Title`);
1473
+ console.log('─'.repeat(80));
1474
+ for (const t of tasks) {
1475
+ const due = t.dueDateTime?.dateTime ? ` (due ${fmtDtTz(t.dueDateTime)})` : '';
1476
+ console.log(`${(t.id || '').padEnd(50)} ${(t.status || '').padEnd(12)} ${t.title || '(untitled)'}${due}`);
1477
+ }
1478
+ return;
1479
+ }
1480
+
1481
+ console.log('To Do: --list-id ID --tasks | --create "TITLE" | --complete TASK_ID');
1482
+ }
1483
+
1164
1484
  // ── CLI Parser ────────────────────────────────────────────────────────────────
1165
1485
  function parseArgs(argv) {
1166
1486
  const BOOL = new Set(['json','unread','sites','chats','teamsList','contacts',
1167
- 'manager','reports','availability','notebooks','list','messages','vtt','help','force']);
1487
+ 'manager','reports','availability','notebooks','list','messages','vtt','help','force',
1488
+ 'plans','buckets','tasks','myTasks','lists','insights']);
1168
1489
  const args = { _: [] };
1169
1490
  let i = 0;
1170
1491
  while (i < argv.length) {
@@ -1216,8 +1537,16 @@ Data:
1216
1537
  onenote [--notebooks] [--sections NOTEBOOK_ID] [--pages SECTION_ID]
1217
1538
  [--read PAGE_ID] [--search QUERY] [--limit N] [--json]
1218
1539
  [--create TITLE --section SECTION_ID [--body CONTENT]]
1540
+ planner [--plans] [--my-tasks]
1541
+ [--plan-id ID --tasks] [--plan-id ID --buckets]
1542
+ [--plan-id ID --create "TITLE" [--bucket-id ID] [--due YYYY-MM-DD] [--assign USER_ID]]
1543
+ [--task-id ID] [--complete TASK_ID] [--json]
1544
+ todo [--lists] [--list-id ID --tasks]
1545
+ [--list-id ID --create "TITLE" [--body TEXT] [--due YYYY-MM-DD]]
1546
+ [--list-id ID --complete TASK_ID] [--json]
1219
1547
  transcripts [--start YYYY-MM-DD] [--end YYYY-MM-DD] [--subject KEYWORD]
1220
1548
  [--meeting ID] [--transcript ID] [--output FILE] [--vtt]
1549
+ [--insights] (AI meeting recap — requires Microsoft 365 Copilot)
1221
1550
 
1222
1551
  Add --json to any command for machine-readable output.
1223
1552
  `);
@@ -1245,6 +1574,8 @@ async function main() {
1245
1574
  people: () => cmdPeople(args),
1246
1575
  org: () => cmdOrg(args),
1247
1576
  onenote: () => cmdOnenote(args),
1577
+ planner: () => cmdPlanner(args),
1578
+ todo: () => cmdTodo(args),
1248
1579
  transcripts: () => cmdTranscripts(args),
1249
1580
  claims: () => cmdClaims(args),
1250
1581
  help: () => { printHelp(); process.exit(0); },
@@ -20,10 +20,18 @@ const ENCRYPTION_KEY = (() => {
20
20
  })();
21
21
 
22
22
  function decrypt(text) {
23
- const [ivHex, encHex] = text.split(':');
24
- const iv = Buffer.from(ivHex, 'hex');
23
+ const parts = text.split(':');
24
+ if (parts.length === 3) {
25
+ const iv = Buffer.from(parts[0], 'hex');
26
+ const authTag = Buffer.from(parts[1], 'hex');
27
+ const d = crypto.createDecipheriv('aes-256-gcm', ENCRYPTION_KEY, iv);
28
+ d.setAuthTag(authTag);
29
+ return d.update(parts[2], 'hex', 'utf8') + d.final('utf8');
30
+ }
31
+ // Legacy CBC format: iv:encrypted (backward compat for existing stored credentials)
32
+ const iv = Buffer.from(parts[0], 'hex');
25
33
  const d = crypto.createDecipheriv('aes-256-cbc', ENCRYPTION_KEY, iv);
26
- return d.update(encHex, 'hex', 'utf8') + d.final('utf8');
34
+ return d.update(parts[1], 'hex', 'utf8') + d.final('utf8');
27
35
  }
28
36
 
29
37
  function urlHash(rawUrl) {
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Shared Claude file-operation extraction.
3
+ *
4
+ * Single source of truth for turning a Claude tool call (tool name + input + the user
5
+ * message's `toolUseResult`) into a file-operation record with line counts. Used by both
6
+ * the live {@link MetricsProcessor} and the session adapter's parse path, so re-parsed
7
+ * (native, untracked) sessions report the same `linesAdded`/`linesRemoved` as live-tracked ones.
8
+ */
9
+ export interface ClaudeFileOperation {
10
+ type: string;
11
+ path?: string;
12
+ format?: string;
13
+ language?: string;
14
+ pattern?: string;
15
+ linesAdded?: number;
16
+ linesRemoved?: number;
17
+ }
18
+ /**
19
+ * Build a file-operation record for a Claude tool call, or `undefined` if the tool is not a
20
+ * file/search tool. Line counts come from the Write content or the Edit `structuredPatch`.
21
+ */
22
+ export declare function extractClaudeFileOperation(toolName: string, input?: {
23
+ file_path?: string;
24
+ path?: string;
25
+ content?: string;
26
+ pattern?: string;
27
+ } | unknown, toolUseResult?: {
28
+ filePath?: string;
29
+ file?: {
30
+ filePath?: string;
31
+ content?: string;
32
+ };
33
+ content?: string;
34
+ structuredPatch?: Array<{
35
+ lines?: unknown[];
36
+ }>;
37
+ } | unknown): ClaudeFileOperation | undefined;
38
+ //# sourceMappingURL=claude-file-operation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-file-operation.d.ts","sourceRoot":"","sources":["../../../../../src/agents/plugins/claude/session/claude-file-operation.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAWD;;;GAGG;AACH,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,MAAM,EAChB,KAAK,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,EAC3F,aAAa,CAAC,EACV;IACE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,KAAK,CAAC;QAAE,KAAK,CAAC,EAAE,OAAO,EAAE,CAAA;KAAE,CAAC,CAAC;CAChD,GACD,OAAO,GACV,mBAAmB,GAAG,SAAS,CAgDjC"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Shared Claude file-operation extraction.
3
+ *
4
+ * Single source of truth for turning a Claude tool call (tool name + input + the user
5
+ * message's `toolUseResult`) into a file-operation record with line counts. Used by both
6
+ * the live {@link MetricsProcessor} and the session adapter's parse path, so re-parsed
7
+ * (native, untracked) sessions report the same `linesAdded`/`linesRemoved` as live-tracked ones.
8
+ */
9
+ import { extractFormat, detectLanguage } from '../../../../utils/file-operations.js';
10
+ /** Tool names that map to a file operation, and their operation type. */
11
+ const TOOL_TYPE_MAP = {
12
+ Read: 'read',
13
+ Write: 'write',
14
+ Edit: 'edit',
15
+ Grep: 'grep',
16
+ Glob: 'glob',
17
+ };
18
+ /**
19
+ * Build a file-operation record for a Claude tool call, or `undefined` if the tool is not a
20
+ * file/search tool. Line counts come from the Write content or the Edit `structuredPatch`.
21
+ */
22
+ export function extractClaudeFileOperation(toolName, input, toolUseResult) {
23
+ const type = TOOL_TYPE_MAP[toolName];
24
+ if (!type) {
25
+ return undefined;
26
+ }
27
+ const inp = (input ?? {});
28
+ const res = (toolUseResult ?? {});
29
+ const fileOp = { type };
30
+ const filePath = res.filePath || res.file?.filePath || inp.file_path || inp.path;
31
+ if (filePath) {
32
+ fileOp.path = filePath;
33
+ fileOp.format = extractFormat(filePath);
34
+ fileOp.language = detectLanguage(filePath);
35
+ }
36
+ else if (inp.pattern) {
37
+ fileOp.pattern = inp.pattern;
38
+ }
39
+ if (toolName === 'Write') {
40
+ const content = res.content || res.file?.content || inp.content;
41
+ if (content) {
42
+ fileOp.linesAdded = content.split('\n').length;
43
+ }
44
+ }
45
+ else if (toolName === 'Edit' && Array.isArray(res.structuredPatch)) {
46
+ let added = 0;
47
+ let removed = 0;
48
+ for (const patch of res.structuredPatch) {
49
+ if (Array.isArray(patch.lines)) {
50
+ for (const line of patch.lines) {
51
+ if (typeof line === 'string') {
52
+ if (line.startsWith('+'))
53
+ added++;
54
+ else if (line.startsWith('-'))
55
+ removed++;
56
+ }
57
+ }
58
+ }
59
+ }
60
+ if (added > 0)
61
+ fileOp.linesAdded = added;
62
+ if (removed > 0)
63
+ fileOp.linesRemoved = removed;
64
+ }
65
+ return fileOp;
66
+ }
67
+ //# sourceMappingURL=claude-file-operation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-file-operation.js","sourceRoot":"","sources":["../../../../../src/agents/plugins/claude/session/claude-file-operation.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,sCAAsC,CAAC;AAYrF,yEAAyE;AACzE,MAAM,aAAa,GAA2B;IAC5C,IAAI,EAAE,MAAM;IACZ,KAAK,EAAE,OAAO;IACd,IAAI,EAAE,MAAM;IACZ,IAAI,EAAE,MAAM;IACZ,IAAI,EAAE,MAAM;CACb,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,0BAA0B,CACxC,QAAgB,EAChB,KAA2F,EAC3F,aAOW;IAEX,MAAM,IAAI,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;IACrC,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,GAAG,GAAG,CAAC,KAAK,IAAI,EAAE,CAA8E,CAAC;IACvG,MAAM,GAAG,GAAG,CAAC,aAAa,IAAI,EAAE,CAK/B,CAAC;IAEF,MAAM,MAAM,GAAwB,EAAE,IAAI,EAAE,CAAC;IAE7C,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,IAAI,EAAE,QAAQ,IAAI,GAAG,CAAC,SAAS,IAAI,GAAG,CAAC,IAAI,CAAC;IACjF,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,CAAC,IAAI,GAAG,QAAQ,CAAC;QACvB,MAAM,CAAC,MAAM,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;QACxC,MAAM,CAAC,QAAQ,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;IAC7C,CAAC;SAAM,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;QACvB,MAAM,CAAC,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC;IAC/B,CAAC;IAED,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,IAAI,EAAE,OAAO,IAAI,GAAG,CAAC,OAAO,CAAC;QAChE,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,CAAC,UAAU,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC;QACjD,CAAC;IACH,CAAC;SAAM,IAAI,QAAQ,KAAK,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,EAAE,CAAC;QACrE,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,IAAI,OAAO,GAAG,CAAC,CAAC;QAChB,KAAK,MAAM,KAAK,IAAI,GAAG,CAAC,eAAe,EAAE,CAAC;YACxC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC/B,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;oBAC/B,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;wBAC7B,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;4BAAE,KAAK,EAAE,CAAC;6BAC7B,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;4BAAE,OAAO,EAAE,CAAC;oBAC3C,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QACD,IAAI,KAAK,GAAG,CAAC;YAAE,MAAM,CAAC,UAAU,GAAG,KAAK,CAAC;QACzC,IAAI,OAAO,GAAG,CAAC;YAAE,MAAM,CAAC,YAAY,GAAG,OAAO,CAAC;IACjD,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -31,9 +31,5 @@ export declare class MetricsProcessor implements SessionProcessor {
31
31
  private extractDeltasFromMessages;
32
32
  private isToolResultMessage;
33
33
  private isSyntheticUserPrompt;
34
- /**
35
- * Extract file operation from tool call (simplified version of legacy logic)
36
- */
37
- private extractFileOperation;
38
34
  }
39
35
  //# sourceMappingURL=claude.metrics-processor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"claude.metrics-processor.d.ts","sourceRoot":"","sources":["../../../../../../src/agents/plugins/claude/session/processors/claude.metrics-processor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,2CAA2C,CAAC;AACvH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gDAAgD,CAAC;AAKpF,qBAAa,gBAAiB,YAAW,gBAAgB;IACvD,QAAQ,CAAC,IAAI,aAAa;IAC1B,QAAQ,CAAC,QAAQ,KAAK;IAEtB,aAAa,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO;IAIxC,OAAO,CAAC,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAY5F;;OAEG;YACW,eAAe;IAqE7B;;OAEG;IACH,OAAO,CAAC,yBAAyB;IAiCjC;;OAEG;IACH,OAAO,CAAC,yBAAyB;IAkMjC,OAAO,CAAC,mBAAmB;IAS3B,OAAO,CAAC,qBAAqB;IAS7B;;OAEG;IACH,OAAO,CAAC,oBAAoB;CAuD7B"}
1
+ {"version":3,"file":"claude.metrics-processor.d.ts","sourceRoot":"","sources":["../../../../../../src/agents/plugins/claude/session/processors/claude.metrics-processor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,2CAA2C,CAAC;AACvH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gDAAgD,CAAC;AAKpF,qBAAa,gBAAiB,YAAW,gBAAgB;IACvD,QAAQ,CAAC,IAAI,aAAa;IAC1B,QAAQ,CAAC,QAAQ,KAAK;IAEtB,aAAa,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO;IAIxC,OAAO,CAAC,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAY5F;;OAEG;YACW,eAAe;IAqE7B;;OAEG;IACH,OAAO,CAAC,yBAAyB;IAiCjC;;OAEG;IACH,OAAO,CAAC,yBAAyB;IAuMjC,OAAO,CAAC,mBAAmB;IAS3B,OAAO,CAAC,qBAAqB;CAS9B"}
@@ -11,7 +11,7 @@
11
11
  * Note: API sync is handled separately by SSO provider's MetricsSyncProcessor
12
12
  */
13
13
  import { logger } from '../../../../../utils/logger.js';
14
- import { extractFormat, detectLanguage } from '../../../../../utils/file-operations.js';
14
+ import { extractClaudeFileOperation } from '../claude-file-operation.js';
15
15
  export class MetricsProcessor {
16
16
  name = 'metrics';
17
17
  priority = 1; // Run first
@@ -227,7 +227,7 @@ export class MetricsProcessor {
227
227
  toolStatus[toolName].success++;
228
228
  // Extract file operations
229
229
  const toolUseResult = toolUseResultMap.get(block.id);
230
- const fileOp = this.extractFileOperation(toolName, block.input, toolUseResult);
230
+ const fileOp = extractClaudeFileOperation(toolName, block.input, toolUseResult);
231
231
  if (fileOp) {
232
232
  fileOperations.push(fileOp);
233
233
  }
@@ -237,6 +237,9 @@ export class MetricsProcessor {
237
237
  }
238
238
  }
239
239
  const recordId = messages[0].uuid;
240
+ const apiErrorMessage = completedMsg.isApiErrorMessage && completedMsg.message?.content?.[0]?.text
241
+ ? completedMsg.message.content[0].text
242
+ : undefined;
240
243
  const delta = {
241
244
  recordId,
242
245
  sessionId,
@@ -245,7 +248,8 @@ export class MetricsProcessor {
245
248
  gitBranch: completedMsg.gitBranch,
246
249
  ...(Object.keys(tools).length > 0 && { tools }),
247
250
  ...(Object.keys(toolStatus).length > 0 && { toolStatus }),
248
- ...(completedMsg.message?.model && { models: [completedMsg.message.model] })
251
+ ...(completedMsg.message?.model && { models: [completedMsg.message.model] }),
252
+ ...(apiErrorMessage && { apiErrorMessage })
249
253
  };
250
254
  if (fileOperations.length > 0) {
251
255
  delta.fileOperations = fileOperations;
@@ -285,58 +289,5 @@ export class MetricsProcessor {
285
289
  const parent = messagesByUuid.get(msg.parentUuid);
286
290
  return this.isToolResultMessage(parent);
287
291
  }
288
- /**
289
- * Extract file operation from tool call (simplified version of legacy logic)
290
- */
291
- extractFileOperation(toolName, input, toolUseResult) {
292
- const typeMap = {
293
- 'Read': 'read',
294
- 'Write': 'write',
295
- 'Edit': 'edit',
296
- 'Grep': 'grep',
297
- 'Glob': 'glob'
298
- };
299
- const type = typeMap[toolName];
300
- if (!type)
301
- return undefined;
302
- const fileOp = { type };
303
- const filePath = toolUseResult?.filePath || toolUseResult?.file?.filePath || input?.file_path || input?.path;
304
- if (filePath) {
305
- fileOp.path = filePath;
306
- fileOp.format = extractFormat(filePath);
307
- fileOp.language = detectLanguage(filePath);
308
- }
309
- else if (input?.pattern) {
310
- fileOp.pattern = input.pattern;
311
- }
312
- if (toolName === 'Write') {
313
- const content = toolUseResult?.content || toolUseResult?.file?.content || input?.content;
314
- if (content) {
315
- const lines = content.split('\n');
316
- fileOp.linesAdded = lines.length;
317
- }
318
- }
319
- else if (toolName === 'Edit' && toolUseResult?.structuredPatch) {
320
- let added = 0;
321
- let removed = 0;
322
- for (const patch of toolUseResult.structuredPatch) {
323
- if (Array.isArray(patch.lines)) {
324
- for (const line of patch.lines) {
325
- if (typeof line === 'string') {
326
- if (line.startsWith('+'))
327
- added++;
328
- else if (line.startsWith('-'))
329
- removed++;
330
- }
331
- }
332
- }
333
- }
334
- if (added > 0)
335
- fileOp.linesAdded = added;
336
- if (removed > 0)
337
- fileOp.linesRemoved = removed;
338
- }
339
- return fileOp;
340
- }
341
292
  }
342
293
  //# sourceMappingURL=claude.metrics-processor.js.map