fraim 2.0.173 → 2.0.174

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.
@@ -21,6 +21,7 @@ function runCleanupArtifactsCommand(options) {
21
21
  process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
22
22
  }
23
23
  exports.cleanupArtifactsCommand = new commander_1.Command('cleanup-artifacts')
24
+ .alias('cleanup-artifact')
24
25
  .description('Evaluate and optionally apply artifact retention cleanup for the current FRAIM workspace')
25
26
  .option('--user <email>', 'Active user email used only for user-scoped cleanup categories')
26
27
  .option('--workspace-root <path>', 'Workspace root containing fraim/config.json')
@@ -8,6 +8,7 @@ exports.resolveArtifactRetentionConfig = resolveArtifactRetentionConfig;
8
8
  exports.runArtifactCleanup = runArtifactCleanup;
9
9
  exports.planArtifactCleanup = planArtifactCleanup;
10
10
  const node_fs_1 = __importDefault(require("node:fs"));
11
+ const node_child_process_1 = require("node:child_process");
11
12
  const node_path_1 = __importDefault(require("node:path"));
12
13
  const project_fraim_paths_1 = require("../core/utils/project-fraim-paths");
13
14
  exports.ARTIFACT_RETENTION_CATEGORIES = [
@@ -96,6 +97,41 @@ function fileMtime(absolutePath) {
96
97
  return undefined;
97
98
  }
98
99
  }
100
+ function parseDate(value) {
101
+ if (!value)
102
+ return undefined;
103
+ const parsed = new Date(value);
104
+ return Number.isNaN(parsed.getTime()) ? undefined : parsed;
105
+ }
106
+ function loadGitLastChangeDates(workspaceRoot, relativeDir) {
107
+ const dates = new Map();
108
+ try {
109
+ const output = (0, node_child_process_1.execFileSync)('git', ['log', '--format=__FRAIM_CLEANUP_DATE__%cI', '--name-only', '--', relativeDir], {
110
+ cwd: workspaceRoot,
111
+ encoding: 'utf8',
112
+ stdio: ['ignore', 'pipe', 'ignore'],
113
+ timeout: 10_000,
114
+ maxBuffer: 80 * 1024 * 1024,
115
+ });
116
+ let currentDate;
117
+ for (const rawLine of output.split(/\r?\n/)) {
118
+ const line = rawLine.trim();
119
+ if (!line)
120
+ continue;
121
+ if (line.startsWith('__FRAIM_CLEANUP_DATE__')) {
122
+ currentDate = parseDate(line.slice('__FRAIM_CLEANUP_DATE__'.length));
123
+ continue;
124
+ }
125
+ const relativePath = line.replace(/\\/g, '/');
126
+ if (currentDate && !dates.has(relativePath))
127
+ dates.set(relativePath, currentDate);
128
+ }
129
+ }
130
+ catch {
131
+ return new Map();
132
+ }
133
+ return dates;
134
+ }
99
135
  function readFrontmatter(content) {
100
136
  const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
101
137
  if (!match)
@@ -124,6 +160,10 @@ function synthesizedDate(absolutePath) {
124
160
  function userScoped(fileName, activeUserEmail) {
125
161
  return fileName.startsWith(`${activeUserEmail}-`);
126
162
  }
163
+ function evidenceEligibilityDate(workspaceRoot, absolutePath, gitDates) {
164
+ const relativePath = normalizeRelative(workspaceRoot, absolutePath);
165
+ return gitDates.get(relativePath) ?? fileMtime(absolutePath);
166
+ }
127
167
  function collectCandidates(workspaceRoot, activeUserEmail) {
128
168
  const candidates = [];
129
169
  const add = (category, absolutePath, eligible, reason, eligibilityDate) => {
@@ -155,23 +195,26 @@ function collectCandidates(workspaceRoot, activeUserEmail) {
155
195
  add('retrospectives', filePath, false, 'Retrospective has no synthesized frontmatter.');
156
196
  }
157
197
  const evidenceDir = node_path_1.default.join(workspaceRoot, 'docs', 'evidence');
198
+ const evidenceGitDates = loadGitLastChangeDates(workspaceRoot, 'docs/evidence');
158
199
  for (const filePath of listFiles(evidenceDir)) {
159
200
  const fileName = node_path_1.default.basename(filePath);
160
201
  const isMarkdown = fileName.endsWith('.md');
161
202
  const isJson = fileName.endsWith('.json');
203
+ const eligibilityDate = evidenceEligibilityDate(workspaceRoot, filePath, evidenceGitDates);
162
204
  if (fileName.includes('cleanup-manifest') || fileName.includes('cleanup_manifest')) {
163
- if (!isMarkdown && !isJson)
164
- continue;
165
- add('cleanup_manifests', filePath, true, 'Cleanup manifest file.', fileMtime(filePath));
205
+ if (isMarkdown || isJson) {
206
+ add('cleanup_manifests', filePath, true, 'Cleanup manifest file.', eligibilityDate);
207
+ }
208
+ else {
209
+ add('evidence', filePath, true, 'Evidence file.', eligibilityDate);
210
+ }
166
211
  continue;
167
212
  }
168
- if (!isMarkdown)
169
- continue;
170
213
  if (fileName.endsWith('-feedback.md')) {
171
- add('feedback', filePath, true, 'Feedback evidence file.', fileMtime(filePath));
214
+ add('feedback', filePath, true, 'Feedback evidence file.', eligibilityDate);
172
215
  continue;
173
216
  }
174
- add('evidence', filePath, true, 'Evidence file.', fileMtime(filePath));
217
+ add('evidence', filePath, true, 'Evidence file.', eligibilityDate);
175
218
  }
176
219
  return candidates;
177
220
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fraim",
3
- "version": "2.0.173",
3
+ "version": "2.0.174",
4
4
  "description": "FRAIM CLI - Framework for Rigor-based AI Management (alias for fraim-framework)",
5
5
  "main": "index.js",
6
6
  "bin": {