prjct-cli 0.52.0 → 0.54.0

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.
@@ -1595,7 +1595,9 @@ var init_config_manager = __esm({
1595
1595
  const now = getTimestamp();
1596
1596
  const localConfig = {
1597
1597
  projectId,
1598
- dataPath: displayPath
1598
+ dataPath: displayPath,
1599
+ showMetrics: true
1600
+ // PRJ-70: default to true for new projects
1599
1601
  };
1600
1602
  await this.writeConfig(projectPath, localConfig);
1601
1603
  const globalConfig = {
@@ -1734,6 +1736,26 @@ var init_config_manager = __esm({
1734
1736
  const config = await this.readConfig(projectPath);
1735
1737
  return this.validateConfig(config);
1736
1738
  }
1739
+ /**
1740
+ * Get showMetrics setting from config.
1741
+ * Defaults to true for new or existing projects without the setting.
1742
+ * @see PRJ-70
1743
+ */
1744
+ async getShowMetrics(projectPath) {
1745
+ const config = await this.readConfig(projectPath);
1746
+ return config?.showMetrics ?? true;
1747
+ }
1748
+ /**
1749
+ * Set showMetrics setting in config.
1750
+ * @see PRJ-70
1751
+ */
1752
+ async setShowMetrics(projectPath, showMetrics) {
1753
+ const config = await this.readConfig(projectPath);
1754
+ if (config) {
1755
+ config.showMetrics = showMetrics;
1756
+ await this.writeConfig(projectPath, config);
1757
+ }
1758
+ }
1737
1759
  /**
1738
1760
  * Get configuration with defaults
1739
1761
  * Returns LOCAL config only (projectId, dataPath)
@@ -2235,10 +2257,10 @@ var init_output = __esm({
2235
2257
  const maxLen = Math.max(title.length, ...lines.map((l) => l.length));
2236
2258
  const border = "\u2500".repeat(maxLen + 2);
2237
2259
  console.log(chalk2.dim(`\u250C${border}\u2510`));
2238
- console.log(chalk2.dim("\u2502") + ` ${chalk2.bold(title.padEnd(maxLen))} ` + chalk2.dim("\u2502"));
2260
+ console.log(`${chalk2.dim("\u2502")} ${chalk2.bold(title.padEnd(maxLen))} ${chalk2.dim("\u2502")}`);
2239
2261
  console.log(chalk2.dim(`\u251C${border}\u2524`));
2240
2262
  for (const line of lines) {
2241
- console.log(chalk2.dim("\u2502") + ` ${line.padEnd(maxLen)} ` + chalk2.dim("\u2502"));
2263
+ console.log(`${chalk2.dim("\u2502")} ${line.padEnd(maxLen)} ${chalk2.dim("\u2502")}`);
2242
2264
  }
2243
2265
  console.log(chalk2.dim(`\u2514${border}\u2518`));
2244
2266
  return this;
@@ -9064,6 +9086,159 @@ var init_memory_system = __esm({
9064
9086
  });
9065
9087
  return scored.filter((m) => m._score > 0).sort((a, b) => b._score - a._score).slice(0, limit).map(({ _score, ...memory }) => memory);
9066
9088
  }
9089
+ /**
9090
+ * Enhanced memory retrieval with domain-based filtering and metrics.
9091
+ * Implements selective memory retrieval based on task relevance.
9092
+ * @see PRJ-107
9093
+ */
9094
+ async getRelevantMemoriesWithMetrics(projectId, query) {
9095
+ const db = await this.load(projectId);
9096
+ const totalMemories = db.memories.length;
9097
+ if (totalMemories === 0) {
9098
+ return {
9099
+ memories: [],
9100
+ metrics: {
9101
+ totalMemories: 0,
9102
+ memoriesConsidered: 0,
9103
+ memoriesReturned: 0,
9104
+ filteringRatio: 0,
9105
+ avgRelevanceScore: 0
9106
+ }
9107
+ };
9108
+ }
9109
+ const maxResults = query.maxResults ?? 10;
9110
+ const minRelevance = query.minRelevance ?? 10;
9111
+ const scored = db.memories.map((memory) => {
9112
+ const breakdown = {
9113
+ domainMatch: 0,
9114
+ tagMatch: 0,
9115
+ recency: 0,
9116
+ confidence: 0,
9117
+ keywords: 0,
9118
+ userTriggered: 0
9119
+ };
9120
+ if (query.taskDomain) {
9121
+ const domainTags = this._getDomainTags(query.taskDomain);
9122
+ const matchingTags = (memory.tags || []).filter((tag) => domainTags.includes(tag));
9123
+ breakdown.domainMatch = Math.min(25, matchingTags.length * 10);
9124
+ }
9125
+ if (query.commandName) {
9126
+ const commandTags = this._getCommandTags(query.commandName);
9127
+ const matchingTags = (memory.tags || []).filter((tag) => commandTags.includes(tag));
9128
+ breakdown.tagMatch = Math.min(20, matchingTags.length * 8);
9129
+ }
9130
+ const age = Date.now() - new Date(memory.updatedAt).getTime();
9131
+ const daysSinceUpdate = age / (1e3 * 60 * 60 * 24);
9132
+ breakdown.recency = Math.max(0, Math.round(15 - daysSinceUpdate * 0.5));
9133
+ if (memory.confidence) {
9134
+ breakdown.confidence = memory.confidence === "high" ? 20 : memory.confidence === "medium" ? 12 : 5;
9135
+ } else if (memory.observationCount) {
9136
+ breakdown.confidence = Math.min(20, memory.observationCount * 3);
9137
+ }
9138
+ if (query.taskDescription) {
9139
+ const keywords = this._extractKeywordsFromText(query.taskDescription);
9140
+ let keywordScore = 0;
9141
+ for (const keyword of keywords) {
9142
+ if (memory.content.toLowerCase().includes(keyword)) keywordScore += 2;
9143
+ if (memory.title.toLowerCase().includes(keyword)) keywordScore += 3;
9144
+ }
9145
+ breakdown.keywords = Math.min(15, keywordScore);
9146
+ }
9147
+ if (memory.userTriggered) {
9148
+ breakdown.userTriggered = 5;
9149
+ }
9150
+ const relevanceScore = breakdown.domainMatch + breakdown.tagMatch + breakdown.recency + breakdown.confidence + breakdown.keywords + breakdown.userTriggered;
9151
+ return {
9152
+ ...memory,
9153
+ relevanceScore,
9154
+ scoreBreakdown: breakdown
9155
+ };
9156
+ });
9157
+ const considered = scored.filter((m) => m.relevanceScore >= minRelevance);
9158
+ const sorted = considered.sort((a, b) => b.relevanceScore - a.relevanceScore);
9159
+ const returned = sorted.slice(0, maxResults);
9160
+ const avgRelevanceScore = returned.length > 0 ? Math.round(returned.reduce((sum, m) => sum + m.relevanceScore, 0) / returned.length) : 0;
9161
+ return {
9162
+ memories: returned,
9163
+ metrics: {
9164
+ totalMemories,
9165
+ memoriesConsidered: considered.length,
9166
+ memoriesReturned: returned.length,
9167
+ filteringRatio: totalMemories > 0 ? returned.length / totalMemories : 0,
9168
+ avgRelevanceScore
9169
+ }
9170
+ };
9171
+ }
9172
+ /**
9173
+ * Map task domain to relevant memory tags.
9174
+ * @see PRJ-107
9175
+ */
9176
+ _getDomainTags(domain) {
9177
+ const domainTagMap = {
9178
+ frontend: [MEMORY_TAGS.CODE_STYLE, MEMORY_TAGS.FILE_STRUCTURE, MEMORY_TAGS.ARCHITECTURE],
9179
+ backend: [
9180
+ MEMORY_TAGS.CODE_STYLE,
9181
+ MEMORY_TAGS.ARCHITECTURE,
9182
+ MEMORY_TAGS.DEPENDENCIES,
9183
+ MEMORY_TAGS.TECH_STACK
9184
+ ],
9185
+ devops: [MEMORY_TAGS.SHIP_WORKFLOW, MEMORY_TAGS.TEST_BEHAVIOR, MEMORY_TAGS.DEPENDENCIES],
9186
+ docs: [MEMORY_TAGS.CODE_STYLE, MEMORY_TAGS.NAMING_CONVENTION],
9187
+ testing: [MEMORY_TAGS.TEST_BEHAVIOR, MEMORY_TAGS.CODE_STYLE],
9188
+ database: [MEMORY_TAGS.ARCHITECTURE, MEMORY_TAGS.NAMING_CONVENTION],
9189
+ general: Object.values(MEMORY_TAGS)
9190
+ };
9191
+ return domainTagMap[domain] || [];
9192
+ }
9193
+ /**
9194
+ * Map command to relevant memory tags.
9195
+ * @see PRJ-107
9196
+ */
9197
+ _getCommandTags(commandName) {
9198
+ const commandTags = {
9199
+ ship: [MEMORY_TAGS.COMMIT_STYLE, MEMORY_TAGS.SHIP_WORKFLOW, MEMORY_TAGS.TEST_BEHAVIOR],
9200
+ feature: [MEMORY_TAGS.ARCHITECTURE, MEMORY_TAGS.CODE_STYLE],
9201
+ done: [MEMORY_TAGS.SHIP_WORKFLOW],
9202
+ analyze: [MEMORY_TAGS.TECH_STACK, MEMORY_TAGS.ARCHITECTURE],
9203
+ spec: [MEMORY_TAGS.ARCHITECTURE, MEMORY_TAGS.CODE_STYLE],
9204
+ task: [MEMORY_TAGS.BRANCH_NAMING, MEMORY_TAGS.CODE_STYLE],
9205
+ sync: [MEMORY_TAGS.TECH_STACK, MEMORY_TAGS.ARCHITECTURE, MEMORY_TAGS.DEPENDENCIES],
9206
+ test: [MEMORY_TAGS.TEST_BEHAVIOR],
9207
+ bug: [MEMORY_TAGS.CODE_STYLE, MEMORY_TAGS.TEST_BEHAVIOR]
9208
+ };
9209
+ return commandTags[commandName] || [];
9210
+ }
9211
+ /**
9212
+ * Extract keywords from text for matching.
9213
+ */
9214
+ _extractKeywordsFromText(text) {
9215
+ const words = text.toLowerCase().split(/\s+/);
9216
+ const stopWords = /* @__PURE__ */ new Set([
9217
+ "the",
9218
+ "a",
9219
+ "an",
9220
+ "is",
9221
+ "are",
9222
+ "to",
9223
+ "for",
9224
+ "and",
9225
+ "or",
9226
+ "in",
9227
+ "on",
9228
+ "at",
9229
+ "by",
9230
+ "with",
9231
+ "from",
9232
+ "as",
9233
+ "it",
9234
+ "this",
9235
+ "that",
9236
+ "be",
9237
+ "have",
9238
+ "has"
9239
+ ]);
9240
+ return words.filter((w) => w.length > 2 && !stopWords.has(w));
9241
+ }
9067
9242
  _extractContextTags(context2) {
9068
9243
  const tags = [];
9069
9244
  const commandTags = {
@@ -9184,6 +9359,14 @@ Context: ${context2}` : ""}`,
9184
9359
  getMemoryStats(projectId) {
9185
9360
  return this._semanticMemories.getMemoryStats(projectId);
9186
9361
  }
9362
+ /**
9363
+ * Get relevant memories with domain-based filtering and metrics.
9364
+ * Implements selective memory retrieval based on task relevance.
9365
+ * @see PRJ-107
9366
+ */
9367
+ getRelevantMemoriesWithMetrics(projectId, query) {
9368
+ return this._semanticMemories.getRelevantMemoriesWithMetrics(projectId, query);
9369
+ }
9187
9370
  // ===========================================================================
9188
9371
  // TIER 1: Session Memory
9189
9372
  // ===========================================================================
@@ -12332,11 +12515,51 @@ var init_prompt_builder = __esm({
12332
12515
  __name(this, "PromptBuilder");
12333
12516
  }
12334
12517
  _checklistsCache = null;
12518
+ _checklistsCacheTime = 0;
12335
12519
  _checklistRoutingCache = null;
12520
+ _checklistRoutingCacheTime = 0;
12336
12521
  _currentContext = null;
12337
12522
  _stateCache = /* @__PURE__ */ new Map();
12338
12523
  _stateCacheTTL = 5e3;
12339
12524
  // 5 seconds
12525
+ _templateCache = /* @__PURE__ */ new Map();
12526
+ TEMPLATE_CACHE_TTL_MS = 6e4;
12527
+ // 60 seconds
12528
+ /**
12529
+ * Get a template with TTL caching.
12530
+ * Returns cached content if within TTL, otherwise loads from disk.
12531
+ * @see PRJ-76
12532
+ */
12533
+ getTemplate(templatePath) {
12534
+ const cached = this._templateCache.get(templatePath);
12535
+ const now = Date.now();
12536
+ if (cached && now - cached.loadedAt < this.TEMPLATE_CACHE_TTL_MS) {
12537
+ return cached.content;
12538
+ }
12539
+ try {
12540
+ if (fs25.existsSync(templatePath)) {
12541
+ const content = fs25.readFileSync(templatePath, "utf-8");
12542
+ this._templateCache.set(templatePath, { content, loadedAt: now });
12543
+ return content;
12544
+ }
12545
+ } catch (error) {
12546
+ if (!isNotFoundError(error)) {
12547
+ console.error(`Template loading warning: ${error.message}`);
12548
+ }
12549
+ }
12550
+ return null;
12551
+ }
12552
+ /**
12553
+ * Clear the template cache (for testing or forced refresh)
12554
+ * @see PRJ-76
12555
+ */
12556
+ clearTemplateCache() {
12557
+ this._templateCache.clear();
12558
+ this._checklistsCache = null;
12559
+ this._checklistsCacheTime = 0;
12560
+ this._checklistRoutingCache = null;
12561
+ this._checklistRoutingCacheTime = 0;
12562
+ }
12340
12563
  /**
12341
12564
  * Reset context (for testing)
12342
12565
  */
@@ -12351,9 +12574,14 @@ var init_prompt_builder = __esm({
12351
12574
  }
12352
12575
  /**
12353
12576
  * Load quality checklists from templates/checklists/
12577
+ * Uses lazy loading with TTL cache.
12578
+ * @see PRJ-76
12354
12579
  */
12355
12580
  loadChecklists() {
12356
- if (this._checklistsCache) return this._checklistsCache;
12581
+ const now = Date.now();
12582
+ if (this._checklistsCache && now - this._checklistsCacheTime < this.TEMPLATE_CACHE_TTL_MS) {
12583
+ return this._checklistsCache;
12584
+ }
12357
12585
  const checklistsDir = path25.join(__dirname, "..", "..", "templates", "checklists");
12358
12586
  const checklists = {};
12359
12587
  try {
@@ -12361,8 +12589,11 @@ var init_prompt_builder = __esm({
12361
12589
  const files = fs25.readdirSync(checklistsDir).filter((f) => f.endsWith(".md"));
12362
12590
  for (const file of files) {
12363
12591
  const name = file.replace(".md", "");
12364
- const content = fs25.readFileSync(path25.join(checklistsDir, file), "utf-8");
12365
- checklists[name] = content;
12592
+ const templatePath = path25.join(checklistsDir, file);
12593
+ const content = this.getTemplate(templatePath);
12594
+ if (content) {
12595
+ checklists[name] = content;
12596
+ }
12366
12597
  }
12367
12598
  }
12368
12599
  } catch (error) {
@@ -12371,6 +12602,7 @@ var init_prompt_builder = __esm({
12371
12602
  }
12372
12603
  }
12373
12604
  this._checklistsCache = checklists;
12605
+ this._checklistsCacheTime = now;
12374
12606
  return checklists;
12375
12607
  }
12376
12608
  /**
@@ -12468,9 +12700,14 @@ var init_prompt_builder = __esm({
12468
12700
  }
12469
12701
  /**
12470
12702
  * Load checklist routing template for Claude to decide which checklists apply
12703
+ * Uses lazy loading with TTL cache.
12704
+ * @see PRJ-76
12471
12705
  */
12472
12706
  loadChecklistRouting() {
12473
- if (this._checklistRoutingCache) return this._checklistRoutingCache;
12707
+ const now = Date.now();
12708
+ if (this._checklistRoutingCache && now - this._checklistRoutingCacheTime < this.TEMPLATE_CACHE_TTL_MS) {
12709
+ return this._checklistRoutingCache;
12710
+ }
12474
12711
  const routingPath = path25.join(
12475
12712
  __dirname,
12476
12713
  "..",
@@ -12479,14 +12716,10 @@ var init_prompt_builder = __esm({
12479
12716
  "agentic",
12480
12717
  "checklist-routing.md"
12481
12718
  );
12482
- try {
12483
- if (fs25.existsSync(routingPath)) {
12484
- this._checklistRoutingCache = fs25.readFileSync(routingPath, "utf-8");
12485
- }
12486
- } catch (error) {
12487
- if (!isNotFoundError(error)) {
12488
- console.error(`Checklist routing warning: ${error.message}`);
12489
- }
12719
+ const content = this.getTemplate(routingPath);
12720
+ if (content) {
12721
+ this._checklistRoutingCache = content;
12722
+ this._checklistRoutingCacheTime = now;
12490
12723
  }
12491
12724
  return this._checklistRoutingCache || null;
12492
12725
  }
@@ -15113,6 +15346,22 @@ var init_memory_service = __esm({
15113
15346
  }
15114
15347
  }
15115
15348
  }
15349
+ /**
15350
+ * Get recent events by projectId (for stats dashboard)
15351
+ * @see PRJ-89
15352
+ */
15353
+ async getRecentEvents(projectId, limit = 100) {
15354
+ try {
15355
+ const memoryPath = path_manager_default.getFilePath(projectId, "memory", "context.jsonl");
15356
+ const entries = await jsonl_helper_default.readJsonLines(memoryPath);
15357
+ return entries.slice(-limit);
15358
+ } catch (error) {
15359
+ if (!isNotFoundError(error) && !(error instanceof SyntaxError)) {
15360
+ console.error(`Memory read error: ${error.message}`);
15361
+ }
15362
+ return [];
15363
+ }
15364
+ }
15116
15365
  };
15117
15366
  memoryService = new MemoryService();
15118
15367
  }
@@ -16583,6 +16832,7 @@ var AnalysisCommands;
16583
16832
  var init_analysis2 = __esm({
16584
16833
  "core/commands/analysis.ts"() {
16585
16834
  "use strict";
16835
+ init_memory_system();
16586
16836
  init_generator();
16587
16837
  init_analyzer2();
16588
16838
  init_command_installer();
@@ -16922,13 +17172,17 @@ ${formatFullDiff(diff)}`);
16922
17172
  };
16923
17173
  }
16924
17174
  /**
16925
- * /p:stats - Value dashboard showing accumulated savings and impact
17175
+ * /p:stats - Session summary and value dashboard
16926
17176
  *
16927
17177
  * Displays:
17178
+ * - Session activity (tasks completed, features shipped today)
17179
+ * - Patterns learned (from memory system)
16928
17180
  * - Token savings (total, compression rate, estimated cost)
16929
17181
  * - Performance metrics (sync count, avg duration)
16930
17182
  * - Agent usage breakdown
16931
17183
  * - 30-day trend visualization
17184
+ *
17185
+ * @see PRJ-89
16932
17186
  */
16933
17187
  async stats(projectPath = process.cwd(), options = {}) {
16934
17188
  try {
@@ -16940,8 +17194,12 @@ ${formatFullDiff(diff)}`);
16940
17194
  }
16941
17195
  const summary = await metricsStorage.getSummary(projectId);
16942
17196
  const dailyStats = await metricsStorage.getDailyStats(projectId, 30);
17197
+ const sessionActivity = await this._getSessionActivity(projectId);
17198
+ const patternsSummary = await memory_system_default.getPatternsSummary(projectId);
16943
17199
  if (options.json) {
16944
17200
  const jsonOutput = {
17201
+ session: sessionActivity,
17202
+ patterns: patternsSummary,
16945
17203
  totalTokensSaved: summary.totalTokensSaved,
16946
17204
  estimatedCostSaved: summary.estimatedCostSaved,
16947
17205
  compressionRate: summary.compressionRate,
@@ -16958,10 +17216,8 @@ ${formatFullDiff(diff)}`);
16958
17216
  const globalPath = path_manager_default.getGlobalProjectPath(projectId);
16959
17217
  let projectName = "Unknown";
16960
17218
  try {
16961
- const fs47 = __require("node:fs/promises");
16962
- const path56 = __require("node:path");
16963
17219
  const projectJson = JSON.parse(
16964
- await fs47.readFile(path56.join(globalPath, "project.json"), "utf-8")
17220
+ await fs34.readFile(path35.join(globalPath, "project.json"), "utf-8")
16965
17221
  );
16966
17222
  projectName = projectJson.name || "Unknown";
16967
17223
  } catch {
@@ -16974,12 +17230,32 @@ ${formatFullDiff(diff)}`);
16974
17230
  }) : "N/A";
16975
17231
  console.log("");
16976
17232
  console.log("\u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E");
16977
- console.log("\u2502 \u{1F4CA} prjct-cli Value Dashboard \u2502");
17233
+ console.log("\u2502 \u{1F4CA} prjct-cli Stats Dashboard \u2502");
16978
17234
  console.log(
16979
17235
  `\u2502 Project: ${projectName.padEnd(20).slice(0, 20)} | Since: ${firstSyncDate.padEnd(12).slice(0, 12)} \u2502`
16980
17236
  );
16981
17237
  console.log("\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F");
16982
17238
  console.log("");
17239
+ console.log("\u{1F3AF} TODAY'S ACTIVITY");
17240
+ if (sessionActivity.sessionDuration) {
17241
+ console.log(` Duration: ${sessionActivity.sessionDuration}`);
17242
+ }
17243
+ console.log(` Tasks completed: ${sessionActivity.tasksCompleted}`);
17244
+ console.log(` Features shipped: ${sessionActivity.featuresShipped}`);
17245
+ if (sessionActivity.agentsUsed.length > 0) {
17246
+ const agentStr = sessionActivity.agentsUsed.slice(0, 3).map((a) => `${a.name} (${a.count}\xD7)`).join(", ");
17247
+ console.log(` Agents used: ${agentStr}`);
17248
+ }
17249
+ console.log("");
17250
+ if (patternsSummary.decisions > 0 || patternsSummary.preferences > 0) {
17251
+ console.log("\u{1F9E0} PATTERNS LEARNED");
17252
+ console.log(
17253
+ ` Decisions: ${patternsSummary.learnedDecisions} confirmed (${patternsSummary.decisions} total)`
17254
+ );
17255
+ console.log(` Preferences: ${patternsSummary.preferences} saved`);
17256
+ console.log(` Workflows: ${patternsSummary.workflows} tracked`);
17257
+ console.log("");
17258
+ }
16983
17259
  console.log("\u{1F4B0} TOKEN SAVINGS");
16984
17260
  console.log(` Total saved: ${this._formatTokens(summary.totalTokensSaved)} tokens`);
16985
17261
  console.log(
@@ -16992,7 +17268,7 @@ ${formatFullDiff(diff)}`);
16992
17268
  console.log(` Avg sync time: ${this._formatDuration(summary.avgSyncDuration)}`);
16993
17269
  console.log("");
16994
17270
  if (summary.topAgents.length > 0) {
16995
- console.log("\u{1F916} AGENT USAGE");
17271
+ console.log("\u{1F916} AGENT USAGE (all time)");
16996
17272
  const totalUsage = summary.topAgents.reduce((sum, a) => sum + a.usageCount, 0);
16997
17273
  for (const agent of summary.topAgents) {
16998
17274
  const pct = totalUsage > 0 ? (agent.usageCount / totalUsage * 100).toFixed(0) : 0;
@@ -17021,20 +17297,68 @@ ${formatFullDiff(diff)}`);
17021
17297
  summary,
17022
17298
  dailyStats,
17023
17299
  projectName,
17024
- firstSyncDate
17300
+ firstSyncDate,
17301
+ sessionActivity,
17302
+ patternsSummary
17025
17303
  );
17026
17304
  console.log(markdown);
17027
17305
  return { success: true, data: { markdown } };
17028
17306
  }
17029
17307
  return {
17030
17308
  success: true,
17031
- data: summary
17309
+ data: { ...summary, session: sessionActivity, patterns: patternsSummary }
17032
17310
  };
17033
17311
  } catch (error) {
17034
17312
  console.error("\u274C Error:", error.message);
17035
17313
  return { success: false, error: error.message };
17036
17314
  }
17037
17315
  }
17316
+ /**
17317
+ * Get session activity stats from today's events
17318
+ * @see PRJ-89
17319
+ */
17320
+ async _getSessionActivity(projectId) {
17321
+ try {
17322
+ const recentHistory = await memoryService.getRecentEvents(projectId, 100);
17323
+ const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
17324
+ const todayEvents = recentHistory.filter((e) => {
17325
+ const ts = e.timestamp || e.ts;
17326
+ return ts?.startsWith(today);
17327
+ });
17328
+ let sessionDuration = null;
17329
+ if (todayEvents.length >= 2) {
17330
+ const timestamps = todayEvents.map((e) => new Date(e.timestamp || e.ts).getTime()).filter((t) => !Number.isNaN(t)).sort((a, b) => a - b);
17331
+ if (timestamps.length >= 2) {
17332
+ const durationMs = timestamps[timestamps.length - 1] - timestamps[0];
17333
+ sessionDuration = date_helper_default.formatDuration(durationMs);
17334
+ }
17335
+ }
17336
+ const tasksCompleted = todayEvents.filter((e) => e.action === "task_completed").length;
17337
+ const featuresShipped = todayEvents.filter((e) => e.action === "feature_shipped").length;
17338
+ const agentCounts = /* @__PURE__ */ new Map();
17339
+ for (const event of todayEvents) {
17340
+ if (event.action === "sync" && Array.isArray(event.subagents)) {
17341
+ for (const agent of event.subagents) {
17342
+ agentCounts.set(agent, (agentCounts.get(agent) || 0) + 1);
17343
+ }
17344
+ }
17345
+ }
17346
+ const agentsUsed = Array.from(agentCounts.entries()).map(([name, count]) => ({ name, count })).sort((a, b) => b.count - a.count);
17347
+ return {
17348
+ sessionDuration,
17349
+ tasksCompleted,
17350
+ featuresShipped,
17351
+ agentsUsed
17352
+ };
17353
+ } catch {
17354
+ return {
17355
+ sessionDuration: null,
17356
+ tasksCompleted: 0,
17357
+ featuresShipped: 0,
17358
+ agentsUsed: []
17359
+ };
17360
+ }
17361
+ }
17038
17362
  // =========== Stats Helper Methods ===========
17039
17363
  _formatTokens(tokens) {
17040
17364
  if (tokens >= 1e6) {
@@ -17061,12 +17385,40 @@ ${formatFullDiff(diff)}`);
17061
17385
  return chars[idx];
17062
17386
  }).join("");
17063
17387
  }
17064
- _generateStatsMarkdown(summary, _dailyStats, projectName, firstSyncDate) {
17388
+ _generateStatsMarkdown(summary, _dailyStats, projectName, firstSyncDate, sessionActivity, patternsSummary) {
17065
17389
  const lines = [];
17066
- lines.push(`# ${projectName} - Value Dashboard`);
17390
+ lines.push(`# ${projectName} - Stats Dashboard`);
17067
17391
  lines.push("");
17068
17392
  lines.push(`_Generated: ${(/* @__PURE__ */ new Date()).toLocaleString()} | Tracking since: ${firstSyncDate}_`);
17069
17393
  lines.push("");
17394
+ if (sessionActivity) {
17395
+ lines.push("## \u{1F3AF} Today's Activity");
17396
+ lines.push("");
17397
+ lines.push(`| Metric | Value |`);
17398
+ lines.push(`|--------|-------|`);
17399
+ if (sessionActivity.sessionDuration) {
17400
+ lines.push(`| Duration | ${sessionActivity.sessionDuration} |`);
17401
+ }
17402
+ lines.push(`| Tasks completed | ${sessionActivity.tasksCompleted} |`);
17403
+ lines.push(`| Features shipped | ${sessionActivity.featuresShipped} |`);
17404
+ if (sessionActivity.agentsUsed.length > 0) {
17405
+ const agentStr = sessionActivity.agentsUsed.slice(0, 3).map((a) => `${a.name} (${a.count}\xD7)`).join(", ");
17406
+ lines.push(`| Agents used | ${agentStr} |`);
17407
+ }
17408
+ lines.push("");
17409
+ }
17410
+ if (patternsSummary && (patternsSummary.decisions > 0 || patternsSummary.preferences > 0)) {
17411
+ lines.push("## \u{1F9E0} Patterns Learned");
17412
+ lines.push("");
17413
+ lines.push(`| Type | Count |`);
17414
+ lines.push(`|------|-------|`);
17415
+ lines.push(
17416
+ `| Decisions | ${patternsSummary.learnedDecisions} confirmed (${patternsSummary.decisions} total) |`
17417
+ );
17418
+ lines.push(`| Preferences | ${patternsSummary.preferences} |`);
17419
+ lines.push(`| Workflows | ${patternsSummary.workflows} |`);
17420
+ lines.push("");
17421
+ }
17070
17422
  lines.push("## \u{1F4B0} Token Savings");
17071
17423
  lines.push("");
17072
17424
  lines.push(`| Metric | Value |`);
@@ -24664,7 +25016,7 @@ var require_package = __commonJS({
24664
25016
  "package.json"(exports, module) {
24665
25017
  module.exports = {
24666
25018
  name: "prjct-cli",
24667
- version: "0.52.0",
25019
+ version: "0.54.0",
24668
25020
  description: "Context layer for AI agents. Project context for Claude Code, Gemini CLI, and more.",
24669
25021
  main: "core/index.ts",
24670
25022
  bin: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prjct-cli",
3
- "version": "0.52.0",
3
+ "version": "0.54.0",
4
4
  "description": "Context layer for AI agents. Project context for Claude Code, Gemini CLI, and more.",
5
5
  "main": "core/index.ts",
6
6
  "bin": {