@ryuenn3123/agentic-senior-core 4.3.14 → 4.4.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.
Files changed (25) hide show
  1. package/.agent-context/prompts/init-project.md +1 -1
  2. package/.agent-context/prompts/refactor.md +1 -0
  3. package/.agent-context/review-checklists/pr-checklist.md +1 -3
  4. package/.agent-context/rules/architecture.md +5 -0
  5. package/AGENTS.md +67 -115
  6. package/bin/agentic-senior-core.js +2 -5
  7. package/lib/cli/adaptive-context/catalog.mjs +4 -0
  8. package/lib/cli/adaptive-context.mjs +87 -301
  9. package/lib/cli/compiler.mjs +6 -389
  10. package/lib/cli/token-optimization.mjs +4 -89
  11. package/package.json +2 -9
  12. package/scripts/context-triggered-audit.mjs +1 -1
  13. package/scripts/{audit-cache-layer-contract.mjs → validate/audits/cache-layer-contract.mjs} +4 -37
  14. package/scripts/{audit-caching-scope-hygiene.mjs → validate/audits/caching-scope-hygiene.mjs} +1 -34
  15. package/scripts/{audit-file-size.mjs → validate/audits/file-size.mjs} +1 -62
  16. package/scripts/{audit-reflection-citations.mjs → validate/audits/reflection-citations.mjs} +2 -35
  17. package/scripts/{audit-release-bundle.mjs → validate/audits/release-bundle.mjs} +1 -36
  18. package/scripts/{audit-rule-id-uniqueness.mjs → validate/audits/rule-id-uniqueness.mjs} +1 -36
  19. package/scripts/validate/config.mjs +0 -18
  20. package/scripts/validate/file-structure.mjs +0 -4
  21. package/scripts/validate/utils.mjs +52 -0
  22. package/scripts/validate.mjs +10 -81
  23. package/lib/cli/audits/typography-palette-anti-repeat-audit.mjs +0 -239
  24. package/lib/cli/commands/audit-design-anti-repeat.mjs +0 -39
  25. package/scripts/audit-typography-palette-anti-repeat.mjs +0 -120
@@ -1,9 +1,4 @@
1
- import {
2
- DOCS_BY_LABEL,
3
- PROMPT_CATALOG,
4
- RULE_FAMILY_CATALOG,
5
- STATE_BY_LABEL,
6
- } from './adaptive-context/catalog.mjs';
1
+ import { DOCS_BY_LABEL, PROMPT_CATALOG, RULE_FAMILY_CATALOG, STATE_BY_LABEL } from './adaptive-context/catalog.mjs';
7
2
  import { CONTEXT_FILE_CATALOG } from './adaptive-context/file-signals.mjs';
8
3
  import { IMPLICATION_CATALOG } from './adaptive-context/implications.mjs';
9
4
 
@@ -17,217 +12,33 @@ export function getRuleFamilyCatalog() {
17
12
  }
18
13
 
19
14
  export function normalizeRequestText(requestText) {
20
- return String(requestText || '')
21
- .trim()
22
- .toLowerCase()
23
- .replace(/\s+/g, ' ');
15
+ return String(requestText || '').trim().toLowerCase().replace(/\s+/g, ' ');
24
16
  }
25
17
 
26
- function findMatchedTriggers(normalizedRequestText, triggers) {
27
- return triggers.filter((triggerText) => triggerMatchesRequest(normalizedRequestText, triggerText));
18
+ function triggerMatchesRequest(text, trigger) {
19
+ if (!text || !trigger) return false;
20
+ return /^[a-z0-9 ]+$/.test(trigger)
21
+ ? new RegExp(`(^|[^a-z0-9])${trigger.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}([^a-z0-9]|$)`).test(text)
22
+ : text.includes(trigger);
28
23
  }
29
24
 
30
- function escapeRegExp(rawText) {
31
- return rawText.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
32
- }
33
-
34
- function triggerMatchesRequest(normalizedRequestText, triggerText) {
35
- if (!normalizedRequestText || !triggerText) {
36
- return false;
37
- }
38
-
39
- if (/^[a-z0-9 ]+$/.test(triggerText)) {
40
- const triggerPattern = new RegExp(`(^|[^a-z0-9])${escapeRegExp(triggerText)}([^a-z0-9]|$)`);
41
- return triggerPattern.test(normalizedRequestText);
42
- }
43
-
44
- return normalizedRequestText.includes(triggerText);
45
- }
46
-
47
- function compareCatalogEntries(leftEntry, rightEntry) {
48
- return leftEntry.priority - rightEntry.priority || leftEntry.label.localeCompare(rightEntry.label);
49
- }
50
-
51
- function normalizeContextFilePath(contextFilePath) {
52
- return String(contextFilePath || '')
53
- .trim()
54
- .replace(/\\/g, '/')
55
- .replace(/\/+/g, '/')
56
- .toLowerCase();
57
- }
58
-
59
- function normalizeContextFiles(contextFiles) {
60
- if (!Array.isArray(contextFiles)) {
61
- return [];
62
- }
63
-
64
- return contextFiles
65
- .map(normalizeContextFilePath)
66
- .filter(Boolean);
67
- }
68
-
69
- function uniqueSortedValues(values) {
70
- return Array.from(new Set(values)).sort((leftValue, rightValue) => leftValue.localeCompare(rightValue));
71
- }
72
-
73
- function collectDocsForLabels(labels) {
74
- const selectedDocs = ['docs/doc-index.md'];
75
-
76
- for (const label of labels) {
77
- selectedDocs.push(...(DOCS_BY_LABEL[label] || []));
78
- }
79
-
80
- return uniqueSortedValues(selectedDocs);
81
- }
82
-
83
- function collectStateForLabels(labels) {
84
- const selectedState = ['.agent-context/state/onboarding-report.json'];
85
-
86
- for (const label of labels) {
87
- selectedState.push(...(STATE_BY_LABEL[label] || []));
88
- }
89
-
90
- return uniqueSortedValues(selectedState);
91
- }
92
-
93
- function collectPromptsForRequest(normalizedRequestText, labels) {
94
- const selectedPrompts = [];
95
-
96
- for (const promptEntry of PROMPT_CATALOG) {
97
- const labelMatched = Array.isArray(promptEntry.labels)
98
- && promptEntry.labels.some((label) => labels.includes(label));
99
- const triggerMatched = findMatchedTriggers(normalizedRequestText, promptEntry.triggers).length > 0;
100
-
101
- if (labelMatched || triggerMatched) {
102
- selectedPrompts.push(promptEntry.promptPath);
103
- }
104
- }
105
-
106
- return uniqueSortedValues(selectedPrompts);
107
- }
108
-
109
- function createRuleEntryMap() {
110
- const ruleEntryMap = new Map();
111
-
112
- for (const ruleFamily of RULE_FAMILY_CATALOG) {
113
- ruleEntryMap.set(ruleFamily.label, {
114
- ...ruleFamily,
115
- matchedTriggers: [],
116
- matchedFiles: [],
117
- matchedImplications: [],
118
- });
119
- }
120
-
121
- return ruleEntryMap;
122
- }
123
-
124
- function getMatchedRuleEntries(ruleEntryMap) {
125
- return Array.from(ruleEntryMap.values())
126
- .filter((ruleFamily) => {
127
- return ruleFamily.matchedTriggers.length > 0
128
- || ruleFamily.matchedFiles.length > 0
129
- || ruleFamily.matchedImplications.length > 0;
130
- })
131
- .sort(compareCatalogEntries);
132
- }
133
-
134
- function pushUniqueSignal(values, nextValue) {
135
- if (nextValue && !values.includes(nextValue)) {
136
- values.push(nextValue);
137
- }
138
- }
25
+ const uniqueSorted = (arr) => Array.from(new Set(arr)).sort((a, b) => a.localeCompare(b));
139
26
 
140
- function applyDirectTextSignals(ruleEntryMap, normalizedRequestText) {
141
- for (const ruleFamily of RULE_FAMILY_CATALOG) {
142
- const matchedTriggers = findMatchedTriggers(normalizedRequestText, ruleFamily.triggers);
143
- const targetRuleEntry = ruleEntryMap.get(ruleFamily.label);
144
-
145
- for (const matchedTrigger of matchedTriggers) {
146
- pushUniqueSignal(targetRuleEntry.matchedTriggers, matchedTrigger);
147
- }
148
- }
149
- }
150
-
151
- function applyContextFileSignals(ruleEntryMap, normalizedContextFiles) {
152
- for (const contextFilePath of normalizedContextFiles) {
153
- for (const fileSignal of CONTEXT_FILE_CATALOG) {
154
- if (!fileSignal.patterns.some((filePattern) => filePattern.test(contextFilePath))) {
155
- continue;
156
- }
157
-
158
- const targetRuleEntry = ruleEntryMap.get(fileSignal.label);
159
- pushUniqueSignal(targetRuleEntry.matchedFiles, contextFilePath);
160
- }
161
- }
27
+ function resolveUncertainty(labels, evidence, text) {
28
+ if (!text || !labels.length) return 'high';
29
+ const hasSparseEvidence = evidence.some(e => (e.matchedTriggers.length + e.matchedFiles.length + e.matchedImplications.length) === 1);
30
+ return (labels.length >= 6 || hasSparseEvidence) ? 'medium' : 'low';
162
31
  }
163
32
 
164
- function applyImplicationSignals(ruleEntryMap, normalizedRequestText) {
165
- const matchedLabelSet = new Set(getMatchedRuleEntries(ruleEntryMap).map((ruleFamily) => ruleFamily.label));
166
-
167
- for (const implicationRule of IMPLICATION_CATALOG) {
168
- const hasRequiredLabel = implicationRule.requiresAnyLabel.some((label) => matchedLabelSet.has(label));
169
- if (!hasRequiredLabel) {
170
- continue;
171
- }
172
-
173
- const matchedTriggers = findMatchedTriggers(normalizedRequestText, implicationRule.triggers);
174
- if (matchedTriggers.length === 0) {
175
- continue;
176
- }
177
-
178
- const targetRuleEntry = ruleEntryMap.get(implicationRule.label);
179
- const implicationDetail = `${implicationRule.reason}: ${matchedTriggers.join(', ')}`;
180
- pushUniqueSignal(targetRuleEntry.matchedImplications, implicationDetail);
181
- matchedLabelSet.add(implicationRule.label);
182
- }
183
- }
184
-
185
- function countEvidenceSignals(evidenceEntry) {
186
- return evidenceEntry.matchedTriggers.length
187
- + evidenceEntry.matchedFiles.length
188
- + evidenceEntry.matchedImplications.length;
189
- }
190
-
191
- function resolveUncertainty(labels, evidenceEntries, normalizedRequestText) {
192
- if (!normalizedRequestText) {
193
- return 'high';
194
- }
195
-
196
- if (labels.length === 0) {
197
- return 'high';
198
- }
199
-
200
- const hasSparseEvidence = evidenceEntries.some((evidenceEntry) => countEvidenceSignals(evidenceEntry) === 1);
201
- const hasManyLabels = labels.length >= 6;
202
-
203
- if (hasManyLabels || hasSparseEvidence) {
204
- return 'medium';
205
- }
206
-
207
- return 'low';
208
- }
209
-
210
- function buildContextBudget(labels, evidenceEntries) {
33
+ function buildContextBudget(labels, evidence) {
211
34
  const selectedRuleCount = labels.length;
212
- const evidenceSignalCount = evidenceEntries.reduce(
213
- (totalCount, evidenceEntry) => totalCount + countEvidenceSignals(evidenceEntry),
214
- 0
215
- );
216
- const overRecommendedRuleCount = Math.max(
217
- 0,
218
- selectedRuleCount - CONTEXT_BUDGET_POLICY.maxRecommendedRuleCount
219
- );
220
- const overFallbackRuleCount = Math.max(
221
- 0,
222
- selectedRuleCount - CONTEXT_BUDGET_POLICY.maxFallbackRuleCount
223
- );
35
+ const evidenceSignalCount = evidence.reduce((sum, e) => sum + e.matchedTriggers.length + e.matchedFiles.length + e.matchedImplications.length, 0);
36
+ const overRecommendedRuleCount = Math.max(0, selectedRuleCount - CONTEXT_BUDGET_POLICY.maxRecommendedRuleCount);
37
+ const overFallbackRuleCount = Math.max(0, selectedRuleCount - CONTEXT_BUDGET_POLICY.maxFallbackRuleCount);
38
+
224
39
  let status = 'within-budget';
225
-
226
- if (overFallbackRuleCount > 0) {
227
- status = 'fallback-required';
228
- } else if (overRecommendedRuleCount > 0) {
229
- status = 'wide-context';
230
- }
40
+ if (overFallbackRuleCount > 0) status = 'fallback-required';
41
+ else if (overRecommendedRuleCount > 0) status = 'wide-context';
231
42
 
232
43
  return {
233
44
  status,
@@ -240,126 +51,101 @@ function buildContextBudget(labels, evidenceEntries) {
240
51
  };
241
52
  }
242
53
 
243
- export function buildSelectedContextManifest(options = {}) {
244
- const {
245
- contextFiles = [],
246
- requestId = 'adhoc-request',
247
- requestText = '',
248
- } = options;
54
+ export function buildSelectedContextManifest({ contextFiles = [], requestId = 'adhoc-request', requestText = '' } = {}) {
55
+ const normText = normalizeRequestText(requestText);
56
+ const normFiles = (Array.isArray(contextFiles) ? contextFiles : [])
57
+ .map(f => String(f || '').trim().replace(/\\/g, '/').replace(/\/+/g, '/').toLowerCase())
58
+ .filter(Boolean);
59
+
60
+ const matchedEntries = RULE_FAMILY_CATALOG.map(rule => {
61
+ const matchedTriggers = rule.triggers.filter(t => triggerMatchesRequest(normText, t));
62
+ const filePatterns = CONTEXT_FILE_CATALOG.find(c => c.label === rule.label)?.patterns || [];
63
+ const matchedFiles = normFiles.filter(f => filePatterns.some(p => p.test(f)));
64
+ return { ...rule, matchedTriggers, matchedFiles, matchedImplications: [] };
65
+ });
66
+
67
+ const matchedLabelSet = new Set(matchedEntries.filter(e => e.matchedTriggers.length || e.matchedFiles.length).map(e => e.label));
68
+
69
+ for (const imp of IMPLICATION_CATALOG) {
70
+ if (imp.requiresAnyLabel.some(l => matchedLabelSet.has(l))) {
71
+ const triggers = imp.triggers.filter(t => triggerMatchesRequest(normText, t));
72
+ if (triggers.length) {
73
+ const target = matchedEntries.find(e => e.label === imp.label);
74
+ if (target) {
75
+ const detail = `${imp.reason}: ${triggers.join(', ')}`;
76
+ if (!target.matchedImplications.includes(detail)) target.matchedImplications.push(detail);
77
+ matchedLabelSet.add(imp.label);
78
+ }
79
+ }
80
+ }
81
+ }
249
82
 
250
- const normalizedRequestText = normalizeRequestText(requestText);
251
- const normalizedContextFiles = normalizeContextFiles(contextFiles);
252
- const ruleEntryMap = createRuleEntryMap();
83
+ const matchedRuleEntries = matchedEntries
84
+ .filter(e => e.matchedTriggers.length || e.matchedFiles.length || e.matchedImplications.length)
85
+ .sort((a, b) => a.priority - b.priority || a.label.localeCompare(b.label));
253
86
 
254
- applyDirectTextSignals(ruleEntryMap, normalizedRequestText);
255
- applyContextFileSignals(ruleEntryMap, normalizedContextFiles);
256
- applyImplicationSignals(ruleEntryMap, normalizedRequestText);
87
+ const labels = matchedRuleEntries.map(e => e.label);
88
+ const evidence = matchedRuleEntries.map(({ label, matchedTriggers, matchedFiles, matchedImplications }) => ({ label, matchedTriggers, matchedFiles, matchedImplications }));
89
+ const uncertainty = resolveUncertainty(labels, evidence, normText);
90
+ const budget = buildContextBudget(labels, evidence);
257
91
 
258
- const matchedRuleEntries = getMatchedRuleEntries(ruleEntryMap);
92
+ const selectedRuleSet = new Set(matchedRuleEntries.map(e => e.rulePath));
259
93
 
260
- const labels = matchedRuleEntries.map((ruleFamily) => ruleFamily.label);
261
- const selectedRules = matchedRuleEntries.map((ruleFamily) => ruleFamily.rulePath);
262
- const selectedRuleSet = new Set(selectedRules);
263
- const skippedRules = RULE_FAMILY_CATALOG
264
- .map((ruleFamily) => ruleFamily.rulePath)
265
- .filter((rulePath) => !selectedRuleSet.has(rulePath))
266
- .sort((leftPath, rightPath) => leftPath.localeCompare(rightPath));
267
- const evidenceEntries = matchedRuleEntries.map((ruleFamily) => ({
268
- label: ruleFamily.label,
269
- matchedTriggers: ruleFamily.matchedTriggers,
270
- matchedFiles: ruleFamily.matchedFiles,
271
- matchedImplications: ruleFamily.matchedImplications,
272
- }));
273
- const uncertainty = resolveUncertainty(labels, evidenceEntries, normalizedRequestText);
274
- const budget = buildContextBudget(labels, evidenceEntries);
94
+ const selectedPrompts = PROMPT_CATALOG
95
+ .filter(p => (Array.isArray(p.labels) && p.labels.some(l => labels.includes(l))) || p.triggers.some(t => triggerMatchesRequest(normText, t)))
96
+ .map(p => p.promptPath);
275
97
 
276
98
  return {
277
99
  schemaVersion: 'adaptive-context-manifest-v1',
278
100
  requestId,
279
- contextFiles: normalizedContextFiles,
101
+ contextFiles: normFiles,
280
102
  labels,
281
- selectedRules,
282
- selectedPrompts: collectPromptsForRequest(normalizedRequestText, labels),
283
- selectedDocs: collectDocsForLabels(labels),
284
- selectedState: collectStateForLabels(labels),
285
- skippedRules,
103
+ selectedRules: matchedRuleEntries.map(e => e.rulePath),
104
+ selectedPrompts: uniqueSorted(selectedPrompts),
105
+ selectedDocs: uniqueSorted(['docs/doc-index.md', ...labels.flatMap(l => DOCS_BY_LABEL[l] || [])]),
106
+ selectedState: uniqueSorted(['.agent-context/state/onboarding-report.json', ...labels.flatMap(l => STATE_BY_LABEL[l] || [])]),
107
+ skippedRules: RULE_FAMILY_CATALOG.map(r => r.rulePath).filter(r => !selectedRuleSet.has(r)).sort((a, b) => a.localeCompare(b)),
286
108
  uncertainty,
287
109
  budget,
288
110
  fallbackRequired: uncertainty === 'high' || budget.status === 'fallback-required',
289
- evidence: evidenceEntries,
111
+ evidence,
290
112
  };
291
113
  }
292
114
 
293
- function compareRequiredLabels(requiredLabels, actualLabels) {
294
- const actualLabelSet = new Set(actualLabels);
295
-
296
- return requiredLabels.filter((requiredLabel) => !actualLabelSet.has(requiredLabel));
297
- }
298
-
299
- function compareExtraLabels(expectedLabels, actualLabels) {
300
- const expectedLabelSet = new Set(expectedLabels);
301
-
302
- return actualLabels.filter((actualLabel) => !expectedLabelSet.has(actualLabel));
303
- }
304
-
305
115
  export function evaluateAdaptiveContextFixtures(fixtures) {
306
- const fixtureResults = fixtures.map((fixtureEntry) => {
307
- const manifest = buildSelectedContextManifest({
308
- requestId: fixtureEntry.id,
309
- requestText: fixtureEntry.requestText,
310
- });
311
- const missedRequiredLabels = compareRequiredLabels(fixtureEntry.requiredLabels, manifest.labels);
312
- const extraLabels = compareExtraLabels(fixtureEntry.allowedLabels || fixtureEntry.requiredLabels, manifest.labels);
313
-
116
+ const results = fixtures.map(fixture => {
117
+ const manifest = buildSelectedContextManifest({ requestId: fixture.id, requestText: fixture.requestText });
118
+ const reqSet = new Set(fixture.requiredLabels);
119
+ const allowSet = new Set(fixture.allowedLabels || fixture.requiredLabels);
314
120
  return {
315
- id: fixtureEntry.id,
316
- passed: missedRequiredLabels.length === 0,
317
- missedRequiredLabels,
318
- extraLabels,
121
+ id: fixture.id,
122
+ passed: fixture.requiredLabels.every(l => manifest.labels.includes(l)),
123
+ missedRequiredLabels: fixture.requiredLabels.filter(l => !manifest.labels.includes(l)),
124
+ extraLabels: manifest.labels.filter(l => !allowSet.has(l)),
319
125
  manifest,
320
126
  };
321
127
  });
322
- const failedFixtures = fixtureResults.filter((fixtureResult) => !fixtureResult.passed);
323
- const missedRequiredLabelCount = fixtureResults.reduce(
324
- (totalCount, fixtureResult) => totalCount + fixtureResult.missedRequiredLabels.length,
325
- 0
326
- );
327
- const extraLabelCount = fixtureResults.reduce(
328
- (totalCount, fixtureResult) => totalCount + fixtureResult.extraLabels.length,
329
- 0
330
- );
331
- const selectedRuleCounts = fixtureResults.map((fixtureResult) => fixtureResult.manifest.budget.selectedRuleCount);
332
- const maxSelectedRuleCount = Math.max(...selectedRuleCounts, 0);
333
- const totalSelectedRuleCount = selectedRuleCounts.reduce(
334
- (totalCount, selectedRuleCount) => totalCount + selectedRuleCount,
335
- 0
336
- );
337
- const overRecommendedFixtureCount = fixtureResults.filter((fixtureResult) => {
338
- return fixtureResult.manifest.budget.status !== 'within-budget';
339
- }).length;
340
- const fallbackFixtureCount = fixtureResults.filter((fixtureResult) => {
341
- return fixtureResult.manifest.fallbackRequired;
342
- }).length;
128
+
129
+ const failed = results.filter(r => !r.passed);
130
+ const selectedRuleCounts = results.map(r => r.manifest.budget.selectedRuleCount);
343
131
 
344
132
  return {
345
133
  reportName: 'adaptive-context-benchmark',
346
134
  generatedAt: new Date().toISOString(),
347
- fixtureCount: fixtureResults.length,
348
- passed: failedFixtures.length === 0,
349
- passedCount: fixtureResults.length - failedFixtures.length,
350
- failedCount: failedFixtures.length,
351
- missedRequiredLabelCount,
352
- extraLabelCount,
135
+ fixtureCount: results.length,
136
+ passed: failed.length === 0,
137
+ passedCount: results.length - failed.length,
138
+ failedCount: failed.length,
139
+ missedRequiredLabelCount: results.reduce((sum, r) => sum + r.missedRequiredLabels.length, 0),
140
+ extraLabelCount: results.reduce((sum, r) => sum + r.extraLabels.length, 0),
353
141
  budgetSummary: {
354
- maxSelectedRuleCount,
355
- averageSelectedRuleCount: fixtureResults.length === 0
356
- ? 0
357
- : Number((totalSelectedRuleCount / fixtureResults.length).toFixed(2)),
358
- overRecommendedFixtureCount,
359
- fallbackFixtureCount,
142
+ maxSelectedRuleCount: Math.max(0, ...selectedRuleCounts),
143
+ averageSelectedRuleCount: results.length ? Number((selectedRuleCounts.reduce((a, b) => a + b, 0) / results.length).toFixed(2)) : 0,
144
+ overRecommendedFixtureCount: results.filter(r => r.manifest.budget.status !== 'within-budget').length,
145
+ fallbackFixtureCount: results.filter(r => r.manifest.fallbackRequired).length,
360
146
  maxRecommendedRuleCount: CONTEXT_BUDGET_POLICY.maxRecommendedRuleCount,
361
147
  maxFallbackRuleCount: CONTEXT_BUDGET_POLICY.maxFallbackRuleCount,
362
148
  },
363
- results: fixtureResults,
149
+ results,
364
150
  };
365
151
  }