@wooojin/forgen 0.2.1 → 0.3.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 (124) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/README.ko.md +25 -14
  3. package/README.md +61 -17
  4. package/agents/analyst.md +48 -4
  5. package/agents/architect.md +39 -4
  6. package/agents/code-reviewer.md +107 -77
  7. package/agents/critic.md +47 -4
  8. package/agents/debugger.md +46 -4
  9. package/agents/designer.md +40 -4
  10. package/agents/executor.md +112 -30
  11. package/agents/explore.md +45 -5
  12. package/agents/git-master.md +48 -4
  13. package/agents/planner.md +121 -18
  14. package/agents/test-engineer.md +58 -4
  15. package/agents/verifier.md +92 -77
  16. package/commands/architecture-decision.md +127 -258
  17. package/commands/calibrate.md +225 -0
  18. package/commands/code-review.md +163 -178
  19. package/commands/compound.md +127 -68
  20. package/commands/deep-interview.md +212 -110
  21. package/commands/docker.md +68 -178
  22. package/commands/forge-loop.md +215 -0
  23. package/commands/learn.md +231 -0
  24. package/commands/retro.md +215 -0
  25. package/commands/ship.md +277 -0
  26. package/dist/cli.js +17 -9
  27. package/dist/core/auto-compound-runner.js +14 -0
  28. package/dist/core/config-injector.d.ts +2 -1
  29. package/dist/core/config-injector.js +2 -1
  30. package/dist/core/dashboard.d.ts +17 -0
  31. package/dist/core/dashboard.js +112 -2
  32. package/dist/core/harness.d.ts +6 -1
  33. package/dist/core/harness.js +75 -19
  34. package/dist/core/paths.d.ts +6 -1
  35. package/dist/core/paths.js +18 -2
  36. package/dist/core/spawn.d.ts +3 -2
  37. package/dist/core/spawn.js +27 -8
  38. package/dist/core/types.d.ts +34 -0
  39. package/dist/engine/compound-lifecycle.d.ts +4 -3
  40. package/dist/engine/compound-lifecycle.js +91 -46
  41. package/dist/engine/meta-learning/adaptive-thresholds.d.ts +20 -0
  42. package/dist/engine/meta-learning/adaptive-thresholds.js +126 -0
  43. package/dist/engine/meta-learning/extraction-tuner.d.ts +15 -0
  44. package/dist/engine/meta-learning/extraction-tuner.js +99 -0
  45. package/dist/engine/meta-learning/matcher-weight-tuner.d.ts +21 -0
  46. package/dist/engine/meta-learning/matcher-weight-tuner.js +151 -0
  47. package/dist/engine/meta-learning/runner.d.ts +14 -0
  48. package/dist/engine/meta-learning/runner.js +90 -0
  49. package/dist/engine/meta-learning/scope-promoter.d.ts +21 -0
  50. package/dist/engine/meta-learning/scope-promoter.js +84 -0
  51. package/dist/engine/meta-learning/session-quality-scorer.d.ts +61 -0
  52. package/dist/engine/meta-learning/session-quality-scorer.js +166 -0
  53. package/dist/engine/meta-learning/types.d.ts +114 -0
  54. package/dist/engine/meta-learning/types.js +43 -0
  55. package/dist/engine/solution-format.d.ts +2 -2
  56. package/dist/engine/solution-format.js +249 -34
  57. package/dist/engine/solution-index.d.ts +1 -1
  58. package/dist/engine/solution-matcher.d.ts +7 -1
  59. package/dist/engine/solution-matcher.js +114 -37
  60. package/dist/fgx.js +12 -8
  61. package/dist/hooks/context-guard.d.ts +5 -0
  62. package/dist/hooks/context-guard.js +118 -2
  63. package/dist/hooks/hooks-generator.d.ts +3 -0
  64. package/dist/hooks/hooks-generator.js +23 -6
  65. package/dist/hooks/keyword-detector.js +16 -100
  66. package/dist/hooks/skill-injector.d.ts +4 -3
  67. package/dist/hooks/skill-injector.js +6 -4
  68. package/dist/host/codex-adapter.d.ts +10 -0
  69. package/dist/host/codex-adapter.js +154 -0
  70. package/dist/mcp/solution-reader.d.ts +5 -5
  71. package/dist/mcp/solution-reader.js +34 -24
  72. package/dist/services/session.d.ts +19 -0
  73. package/dist/services/session.js +62 -0
  74. package/hooks/hooks.json +2 -2
  75. package/package.json +2 -1
  76. package/skills/architecture-decision/SKILL.md +113 -257
  77. package/skills/calibrate/SKILL.md +207 -0
  78. package/skills/code-review/SKILL.md +151 -178
  79. package/skills/compound/SKILL.md +126 -68
  80. package/skills/deep-interview/SKILL.md +210 -110
  81. package/skills/docker/SKILL.md +57 -179
  82. package/skills/forge-loop/SKILL.md +198 -0
  83. package/skills/learn/SKILL.md +216 -0
  84. package/skills/retro/SKILL.md +199 -0
  85. package/skills/ship/SKILL.md +259 -0
  86. package/agents/code-simplifier.md +0 -197
  87. package/agents/performance-reviewer.md +0 -172
  88. package/agents/qa-tester.md +0 -158
  89. package/agents/refactoring-expert.md +0 -168
  90. package/agents/scientist.md +0 -144
  91. package/agents/security-reviewer.md +0 -137
  92. package/agents/writer.md +0 -184
  93. package/commands/api-design.md +0 -268
  94. package/commands/ci-cd.md +0 -270
  95. package/commands/database.md +0 -263
  96. package/commands/debug-detective.md +0 -99
  97. package/commands/documentation.md +0 -276
  98. package/commands/ecomode.md +0 -51
  99. package/commands/frontend.md +0 -271
  100. package/commands/git-master.md +0 -90
  101. package/commands/incident-response.md +0 -292
  102. package/commands/migrate.md +0 -101
  103. package/commands/performance.md +0 -288
  104. package/commands/refactor.md +0 -105
  105. package/commands/security-review.md +0 -288
  106. package/commands/specify.md +0 -128
  107. package/commands/tdd.md +0 -183
  108. package/commands/testing-strategy.md +0 -265
  109. package/skills/api-design/SKILL.md +0 -262
  110. package/skills/ci-cd/SKILL.md +0 -264
  111. package/skills/database/SKILL.md +0 -257
  112. package/skills/debug-detective/SKILL.md +0 -95
  113. package/skills/documentation/SKILL.md +0 -270
  114. package/skills/ecomode/SKILL.md +0 -46
  115. package/skills/frontend/SKILL.md +0 -265
  116. package/skills/git-master/SKILL.md +0 -86
  117. package/skills/incident-response/SKILL.md +0 -286
  118. package/skills/migrate/SKILL.md +0 -96
  119. package/skills/performance/SKILL.md +0 -282
  120. package/skills/refactor/SKILL.md +0 -100
  121. package/skills/security-review/SKILL.md +0 -282
  122. package/skills/specify/SKILL.md +0 -122
  123. package/skills/tdd/SKILL.md +0 -178
  124. package/skills/testing-strategy/SKILL.md +0 -260
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Forgen Meta-Learning — Session Quality Scorer (Feature 1)
3
+ *
4
+ * Joins existing data sources to compute a per-session quality score.
5
+ * This score feeds other meta-learning features (matcher tuning, thresholds).
6
+ *
7
+ * Data sources:
8
+ * - injection-cache-{sessionId}.json → injected solutions
9
+ * - modified-files-{sessionId}.json → drift state
10
+ * - implicit-feedback.jsonl → revert/drift events
11
+ * - me/behavior/*.json → correction evidence
12
+ * - state/sessions/{sessionId}.json → session metadata
13
+ */
14
+ import type { SessionQualityScore } from './types.js';
15
+ interface InjectionCacheData {
16
+ injected: string[];
17
+ totalInjectedChars: number;
18
+ updatedAt: string;
19
+ /** Tag snapshot per solution (backfill feature) */
20
+ tags?: Record<string, string[]>;
21
+ }
22
+ interface DriftState {
23
+ sessionId: string;
24
+ totalEdits: number;
25
+ totalReverts: number;
26
+ ewmaEditRate: number;
27
+ ewmaRevertRate: number;
28
+ lastWarningAt: number | null;
29
+ lastCriticalAt: number | null;
30
+ hardCapReached: boolean;
31
+ }
32
+ interface ImplicitFeedbackEntry {
33
+ type: string;
34
+ sessionId?: string;
35
+ at: string;
36
+ [key: string]: unknown;
37
+ }
38
+ export declare function loadInjectionCache(sessionId: string): InjectionCacheData | null;
39
+ export declare function loadDriftState(sessionId: string): DriftState | null;
40
+ export declare function loadImplicitFeedback(sessionId: string): ImplicitFeedbackEntry[];
41
+ export declare function loadSessionCorrections(sessionId: string): number;
42
+ /**
43
+ * Compute overall session quality score (0-100, higher = better).
44
+ *
45
+ * Formula:
46
+ * 100
47
+ * - (correctionRate × 15) // each correction/prompt penalizes 15pts
48
+ * - (driftScore × 0.3) // drift 0-100 maps to 0-30 penalty
49
+ * - (revertCount × 5) // each revert penalizes 5pts
50
+ * + (solutionEffectiveness × 20) // good solution usage boosts 0-20pts
51
+ */
52
+ export declare function computeOverallScore(correctionRate: number, driftScore: number, revertCount: number, solutionEffectiveness: number): number;
53
+ /**
54
+ * Score a session's quality by joining all available data sources.
55
+ * Returns null if insufficient data (no session state found).
56
+ */
57
+ export declare function scoreSession(sessionId: string): SessionQualityScore | null;
58
+ export declare function saveSessionQuality(score: SessionQualityScore, baseDir?: string): void;
59
+ export declare function loadSessionQuality(sessionId: string, baseDir?: string): SessionQualityScore | null;
60
+ export declare function loadRecentQualityScores(limit?: number, baseDir?: string): SessionQualityScore[];
61
+ export {};
@@ -0,0 +1,166 @@
1
+ /**
2
+ * Forgen Meta-Learning — Session Quality Scorer (Feature 1)
3
+ *
4
+ * Joins existing data sources to compute a per-session quality score.
5
+ * This score feeds other meta-learning features (matcher tuning, thresholds).
6
+ *
7
+ * Data sources:
8
+ * - injection-cache-{sessionId}.json → injected solutions
9
+ * - modified-files-{sessionId}.json → drift state
10
+ * - implicit-feedback.jsonl → revert/drift events
11
+ * - me/behavior/*.json → correction evidence
12
+ * - state/sessions/{sessionId}.json → session metadata
13
+ */
14
+ import * as fs from 'node:fs';
15
+ import * as path from 'node:path';
16
+ import { ME_BEHAVIOR, STATE_DIR } from '../../core/paths.js';
17
+ import { safeReadJSON } from '../../hooks/shared/atomic-write.js';
18
+ function sanitizeId(id) {
19
+ return id.replace(/[^a-zA-Z0-9_-]/g, '_');
20
+ }
21
+ export function loadInjectionCache(sessionId) {
22
+ const cachePath = path.join(STATE_DIR, `injection-cache-${sanitizeId(sessionId)}.json`);
23
+ return safeReadJSON(cachePath, null);
24
+ }
25
+ function loadSolutionCache(sessionId) {
26
+ const cachePath = path.join(STATE_DIR, `solution-cache-${sanitizeId(sessionId)}.json`);
27
+ return safeReadJSON(cachePath, null);
28
+ }
29
+ export function loadDriftState(sessionId) {
30
+ const statePath = path.join(STATE_DIR, `modified-files-${sanitizeId(sessionId)}.json`);
31
+ const data = safeReadJSON(statePath, null);
32
+ return data?.drift ?? null;
33
+ }
34
+ export function loadImplicitFeedback(sessionId) {
35
+ const logPath = path.join(STATE_DIR, 'implicit-feedback.jsonl');
36
+ try {
37
+ if (!fs.existsSync(logPath))
38
+ return [];
39
+ const lines = fs.readFileSync(logPath, 'utf-8').split('\n').filter(Boolean);
40
+ const entries = [];
41
+ for (const line of lines) {
42
+ try {
43
+ const entry = JSON.parse(line);
44
+ if (entry.sessionId === sessionId)
45
+ entries.push(entry);
46
+ }
47
+ catch {
48
+ /* skip malformed lines */
49
+ }
50
+ }
51
+ return entries;
52
+ }
53
+ catch {
54
+ return [];
55
+ }
56
+ }
57
+ export function loadSessionCorrections(sessionId) {
58
+ try {
59
+ if (!fs.existsSync(ME_BEHAVIOR))
60
+ return 0;
61
+ let count = 0;
62
+ for (const file of fs.readdirSync(ME_BEHAVIOR)) {
63
+ if (!file.endsWith('.json'))
64
+ continue;
65
+ const data = safeReadJSON(path.join(ME_BEHAVIOR, file), null);
66
+ if (data?.session_id === sessionId && data?.type === 'explicit_correction') {
67
+ count++;
68
+ }
69
+ }
70
+ return count;
71
+ }
72
+ catch {
73
+ return 0;
74
+ }
75
+ }
76
+ function loadToolCallCount(sessionId) {
77
+ const statePath = path.join(STATE_DIR, `modified-files-${sanitizeId(sessionId)}.json`);
78
+ const data = safeReadJSON(statePath, null);
79
+ return data?.toolCallCount ?? 0;
80
+ }
81
+ // ── Score computation ──
82
+ /**
83
+ * Compute overall session quality score (0-100, higher = better).
84
+ *
85
+ * Formula:
86
+ * 100
87
+ * - (correctionRate × 15) // each correction/prompt penalizes 15pts
88
+ * - (driftScore × 0.3) // drift 0-100 maps to 0-30 penalty
89
+ * - (revertCount × 5) // each revert penalizes 5pts
90
+ * + (solutionEffectiveness × 20) // good solution usage boosts 0-20pts
91
+ */
92
+ export function computeOverallScore(correctionRate, driftScore, revertCount, solutionEffectiveness) {
93
+ const raw = 100 - correctionRate * 15 - driftScore * 0.3 - revertCount * 5 + solutionEffectiveness * 20;
94
+ return Math.max(0, Math.min(100, Math.round(raw * 100) / 100));
95
+ }
96
+ // ── Main entry ──
97
+ /**
98
+ * Score a session's quality by joining all available data sources.
99
+ * Returns null if insufficient data (no session state found).
100
+ */
101
+ export function scoreSession(sessionId) {
102
+ // Load injected solutions — try both caches
103
+ const injectionCache = loadInjectionCache(sessionId);
104
+ const solutionCache = loadSolutionCache(sessionId);
105
+ const injectedSolutions = injectionCache?.injected ?? solutionCache?.injected ?? [];
106
+ // Load drift state
107
+ const drift = loadDriftState(sessionId);
108
+ const driftScore = drift ? Math.min(100, drift.ewmaEditRate * 65 + drift.ewmaRevertRate * 35) : 0;
109
+ const revertCount = drift?.totalReverts ?? 0;
110
+ // Count corrections
111
+ const corrections = loadSessionCorrections(sessionId);
112
+ const toolCallCount = loadToolCallCount(sessionId);
113
+ // Use toolCallCount as proxy for prompt count (each prompt leads to tool calls)
114
+ const promptEstimate = Math.max(1, Math.ceil(toolCallCount / 3));
115
+ const correctionRate = corrections / promptEstimate;
116
+ // Solution effectiveness: we can only measure at session level
117
+ // by checking how many injected solutions have reflected > 0.
118
+ // For per-session granularity, count revert events as negative signal.
119
+ const implicitFeedback = loadImplicitFeedback(sessionId);
120
+ const revertEvents = implicitFeedback.filter((e) => e.type === 'revert_detected').length;
121
+ // Effectiveness: 1 - (negative signals / total injections), clamped to [0, 1]
122
+ const solutionEffectiveness = injectedSolutions.length > 0
123
+ ? Math.max(0, Math.min(1, 1 - revertEvents / injectedSolutions.length))
124
+ : 0;
125
+ const overallScore = computeOverallScore(correctionRate, driftScore, revertCount, solutionEffectiveness);
126
+ return {
127
+ sessionId,
128
+ correctionRate: Math.round(correctionRate * 1000) / 1000,
129
+ driftScore: Math.round(driftScore * 100) / 100,
130
+ revertCount,
131
+ solutionEffectiveness: Math.round(solutionEffectiveness * 1000) / 1000,
132
+ overallScore,
133
+ injectedSolutions,
134
+ computedAt: new Date().toISOString(),
135
+ };
136
+ }
137
+ // ── Persistence ──
138
+ export function saveSessionQuality(score, baseDir) {
139
+ const dir = baseDir ?? path.join(STATE_DIR, 'session-quality');
140
+ fs.mkdirSync(dir, { recursive: true });
141
+ const filePath = path.join(dir, `${sanitizeId(score.sessionId)}.json`);
142
+ fs.writeFileSync(filePath, JSON.stringify(score, null, 2));
143
+ }
144
+ export function loadSessionQuality(sessionId, baseDir) {
145
+ const dir = baseDir ?? path.join(STATE_DIR, 'session-quality');
146
+ const filePath = path.join(dir, `${sanitizeId(sessionId)}.json`);
147
+ return safeReadJSON(filePath, null);
148
+ }
149
+ export function loadRecentQualityScores(limit = 10, baseDir) {
150
+ const dir = baseDir ?? path.join(STATE_DIR, 'session-quality');
151
+ try {
152
+ if (!fs.existsSync(dir))
153
+ return [];
154
+ const files = fs.readdirSync(dir).filter((f) => f.endsWith('.json'));
155
+ const scores = [];
156
+ for (const file of files) {
157
+ const score = safeReadJSON(path.join(dir, file), null);
158
+ if (score)
159
+ scores.push(score);
160
+ }
161
+ return scores.sort((a, b) => b.computedAt.localeCompare(a.computedAt)).slice(0, limit);
162
+ }
163
+ catch {
164
+ return [];
165
+ }
166
+ }
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Forgen Meta-Learning — Shared Types
3
+ *
4
+ * HyperAgents-inspired self-tuning layer above the compound system.
5
+ * All types consumed by the meta-learning runner and its sub-modules.
6
+ */
7
+ export interface SessionQualityScore {
8
+ sessionId: string;
9
+ /** corrections per prompt in this session */
10
+ correctionRate: number;
11
+ /** final EWMA drift score (0-100) */
12
+ driftScore: number;
13
+ /** total reverts detected */
14
+ revertCount: number;
15
+ /** reflected / injected ratio (0-1, NaN → 0 if no injections) */
16
+ solutionEffectiveness: number;
17
+ /** composite score 0-100 (higher = better) */
18
+ overallScore: number;
19
+ /** which solutions were injected this session */
20
+ injectedSolutions: string[];
21
+ computedAt: string;
22
+ }
23
+ export interface MatcherWeights {
24
+ tfidf: number;
25
+ bm25: number;
26
+ bigram: number;
27
+ updatedAt: string;
28
+ /** how many solutions informed this tuning */
29
+ sampleSize: number;
30
+ /** monotonic version for rollback detection */
31
+ version: number;
32
+ /** original defaults for fallback */
33
+ defaults: {
34
+ tfidf: number;
35
+ bm25: number;
36
+ bigram: number;
37
+ };
38
+ }
39
+ export interface PromotionThresholds {
40
+ reflected: number;
41
+ sessions: number;
42
+ reExtracted: number;
43
+ }
44
+ export interface AdaptiveLifecycleThresholds {
45
+ experiment: PromotionThresholds;
46
+ candidate: PromotionThresholds;
47
+ verified: PromotionThresholds & {
48
+ negative: number;
49
+ };
50
+ /** solutions per week */
51
+ learningVelocity: number;
52
+ updatedAt: string;
53
+ sampleSize: number;
54
+ defaults: {
55
+ experiment: PromotionThresholds;
56
+ candidate: PromotionThresholds;
57
+ verified: PromotionThresholds & {
58
+ negative: number;
59
+ };
60
+ };
61
+ }
62
+ export interface ExtractionBias {
63
+ typeWeights: Record<string, number>;
64
+ updatedAt: string;
65
+ sampleSize: number;
66
+ }
67
+ export interface ProjectUsageEntry {
68
+ projects: string[];
69
+ updatedAt: string;
70
+ }
71
+ export interface ProjectUsageMap {
72
+ solutions: Record<string, ProjectUsageEntry>;
73
+ }
74
+ export interface MetaLearningFeatures {
75
+ sessionQualityScorer: boolean;
76
+ matcherWeightTuning: boolean;
77
+ scopeAutoPromotion: boolean;
78
+ adaptiveThresholds: boolean;
79
+ extractionTuning: boolean;
80
+ }
81
+ export interface ColdStartConfig {
82
+ minSessionsForQuality: number;
83
+ minSolutionsForMatcher: number;
84
+ minSolutionsForThresholds: number;
85
+ minSolutionsForExtraction: number;
86
+ minProjectsForScope: number;
87
+ }
88
+ export interface GuardrailConfig {
89
+ weightFloor: number;
90
+ weightCeiling: number;
91
+ maxWeightDelta: number;
92
+ thresholdFloor: number;
93
+ thresholdCeiling: number;
94
+ maxThresholdDelta: number;
95
+ degradationThreshold: number;
96
+ }
97
+ export interface MetaLearningConfig {
98
+ enabled: boolean;
99
+ features: MetaLearningFeatures;
100
+ coldStart: ColdStartConfig;
101
+ guardrails: GuardrailConfig;
102
+ }
103
+ export interface MetaLearningResult {
104
+ skipped?: boolean;
105
+ reason?: string;
106
+ qualityScore?: SessionQualityScore | null;
107
+ matcherWeights?: MatcherWeights | null;
108
+ scopePromotions?: string[];
109
+ thresholds?: AdaptiveLifecycleThresholds | null;
110
+ extractionBias?: ExtractionBias | null;
111
+ }
112
+ export declare const DEFAULT_CONFIG: MetaLearningConfig;
113
+ export declare const DEFAULT_MATCHER_WEIGHTS: MatcherWeights['defaults'];
114
+ export declare const DEFAULT_PROMOTION_THRESHOLDS: AdaptiveLifecycleThresholds['defaults'];
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Forgen Meta-Learning — Shared Types
3
+ *
4
+ * HyperAgents-inspired self-tuning layer above the compound system.
5
+ * All types consumed by the meta-learning runner and its sub-modules.
6
+ */
7
+ // ── Defaults ──
8
+ export const DEFAULT_CONFIG = {
9
+ enabled: false,
10
+ features: {
11
+ sessionQualityScorer: true,
12
+ matcherWeightTuning: true,
13
+ scopeAutoPromotion: true,
14
+ adaptiveThresholds: true,
15
+ extractionTuning: true,
16
+ },
17
+ coldStart: {
18
+ minSessionsForQuality: 1,
19
+ minSolutionsForMatcher: 10,
20
+ minSolutionsForThresholds: 15,
21
+ minSolutionsForExtraction: 20,
22
+ minProjectsForScope: 3,
23
+ },
24
+ guardrails: {
25
+ weightFloor: 0.1,
26
+ weightCeiling: 0.7,
27
+ maxWeightDelta: 0.05,
28
+ thresholdFloor: 2,
29
+ thresholdCeiling: 15,
30
+ maxThresholdDelta: 1,
31
+ degradationThreshold: 0.3,
32
+ },
33
+ };
34
+ export const DEFAULT_MATCHER_WEIGHTS = {
35
+ tfidf: 0.5,
36
+ bm25: 0.3,
37
+ bigram: 0.2,
38
+ };
39
+ export const DEFAULT_PROMOTION_THRESHOLDS = {
40
+ experiment: { reflected: 3, sessions: 3, reExtracted: 2 },
41
+ candidate: { reflected: 4, sessions: 3, reExtracted: 2 },
42
+ verified: { reflected: 8, sessions: 5, reExtracted: 2, negative: 1 },
43
+ };
@@ -13,7 +13,7 @@ export interface SolutionFrontmatter {
13
13
  status: SolutionStatus;
14
14
  confidence: number;
15
15
  type: SolutionType;
16
- scope: 'me' | 'team' | 'project';
16
+ scope: 'me' | 'team' | 'project' | 'universal';
17
17
  tags: string[];
18
18
  identifiers: string[];
19
19
  evidence: SolutionEvidence;
@@ -33,7 +33,7 @@ export interface SolutionIndexEntry {
33
33
  status: SolutionStatus;
34
34
  confidence: number;
35
35
  type: SolutionType;
36
- scope: 'me' | 'team' | 'project';
36
+ scope: 'me' | 'team' | 'project' | 'universal';
37
37
  tags: string[];
38
38
  /**
39
39
  * Pre-expanded tag set, computed at index build time via the term normalizer.