memorix 1.2.0 → 1.2.2
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.
- package/CHANGELOG.md +30 -1
- package/README.md +18 -4
- package/README.zh-CN.md +18 -4
- package/TEAM.md +86 -86
- package/dist/cli/index.js +15919 -14055
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +1997 -1021
- package/dist/index.js.map +1 -1
- package/dist/maintenance-runner.d.ts +1 -1
- package/dist/maintenance-runner.js +8481 -8005
- package/dist/maintenance-runner.js.map +1 -1
- package/dist/memcode-runtime/CHANGELOG.md +30 -1
- package/dist/sdk.d.ts +7 -2
- package/dist/sdk.js +2022 -1024
- package/dist/sdk.js.map +1 -1
- package/dist/types.d.ts +49 -1
- package/dist/types.js.map +1 -1
- package/docs/1.2.2-MEMORY-CONTROL-PLANE.md +434 -0
- package/docs/AGENT_OPERATOR_PLAYBOOK.md +4 -0
- package/docs/API_REFERENCE.md +27 -5
- package/docs/DESIGN_DECISIONS.md +357 -357
- package/docs/DEVELOPMENT.md +4 -0
- package/docs/README.md +1 -1
- package/docs/SETUP.md +7 -1
- package/docs/dev-log/progress.txt +91 -11
- package/docs/knowledge/workflows/memorix-release.md +57 -0
- package/package.json +1 -1
- package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
- package/src/audit/index.ts +156 -156
- package/src/cli/command-guide.ts +192 -0
- package/src/cli/commands/audit-list.ts +89 -89
- package/src/cli/commands/audit.ts +9 -4
- package/src/cli/commands/background.ts +659 -659
- package/src/cli/commands/cleanup.ts +5 -1
- package/src/cli/commands/codegraph.ts +17 -8
- package/src/cli/commands/context.ts +3 -2
- package/src/cli/commands/doctor.ts +4 -2
- package/src/cli/commands/explain.ts +9 -3
- package/src/cli/commands/formation.ts +48 -48
- package/src/cli/commands/git-hook-install.ts +111 -111
- package/src/cli/commands/handoff.ts +75 -61
- package/src/cli/commands/hooks-status.ts +63 -63
- package/src/cli/commands/identity.ts +116 -0
- package/src/cli/commands/ingest-commit.ts +153 -153
- package/src/cli/commands/ingest-image.ts +71 -69
- package/src/cli/commands/ingest-log.ts +180 -180
- package/src/cli/commands/ingest.ts +44 -44
- package/src/cli/commands/integrate-shared.ts +15 -15
- package/src/cli/commands/knowledge.ts +40 -0
- package/src/cli/commands/lock.ts +93 -92
- package/src/cli/commands/memory.ts +58 -21
- package/src/cli/commands/message.ts +123 -118
- package/src/cli/commands/operator-shared.ts +98 -3
- package/src/cli/commands/poll.ts +74 -64
- package/src/cli/commands/purge-all-memory.ts +85 -85
- package/src/cli/commands/purge-project-memory.ts +83 -83
- package/src/cli/commands/reasoning.ts +135 -121
- package/src/cli/commands/retention.ts +9 -4
- package/src/cli/commands/serve-http.ts +22 -43
- package/src/cli/commands/serve-shared.ts +118 -118
- package/src/cli/commands/session.ts +29 -3
- package/src/cli/commands/setup.ts +9 -3
- package/src/cli/commands/skills.ts +124 -119
- package/src/cli/commands/status.ts +4 -3
- package/src/cli/commands/task.ts +193 -184
- package/src/cli/commands/team.ts +14 -10
- package/src/cli/commands/transfer.ts +108 -55
- package/src/cli/commands/uninstall-project-artifacts.ts +85 -85
- package/src/cli/identity.ts +89 -0
- package/src/cli/index.ts +96 -19
- package/src/cli/invocation.ts +115 -0
- package/src/cli/tui/ChatView.tsx +234 -234
- package/src/cli/tui/CommandBar.tsx +312 -312
- package/src/cli/tui/ContextRail.tsx +118 -118
- package/src/cli/tui/HeaderBar.tsx +72 -72
- package/src/cli/tui/LogoBanner.tsx +51 -51
- package/src/cli/tui/Sidebar.tsx +179 -179
- package/src/cli/tui/chat-service.ts +41 -18
- package/src/cli/tui/data.ts +23 -44
- package/src/cli/tui/index.ts +41 -41
- package/src/cli/tui/markdown-render.tsx +371 -371
- package/src/cli/tui/operator-context.ts +60 -0
- package/src/cli/tui/use-mouse.ts +157 -157
- package/src/cli/tui/useNavigation.ts +56 -56
- package/src/cli/tui/views/MemoryView.tsx +10 -8
- package/src/cli/update-checker.ts +211 -211
- package/src/cli/version.ts +7 -7
- package/src/cli/workbench.ts +1 -1
- package/src/codegraph/auto-context.ts +34 -17
- package/src/codegraph/context-pack.ts +1 -0
- package/src/codegraph/current-facts.ts +19 -1
- package/src/codegraph/project-context.ts +2 -0
- package/src/codegraph/task-lens.ts +49 -5
- package/src/compact/engine.ts +26 -10
- package/src/compact/index-format.ts +25 -2
- package/src/compact/token-budget.ts +74 -74
- package/src/dashboard/project-classification.ts +64 -64
- package/src/dashboard/server.ts +58 -52
- package/src/embedding/fastembed-provider.ts +142 -142
- package/src/embedding/transformers-provider.ts +111 -111
- package/src/git/extractor.ts +209 -209
- package/src/git/hooks-path.ts +85 -85
- package/src/hooks/admission.ts +117 -0
- package/src/hooks/handler.ts +98 -91
- package/src/hooks/pattern-detector.ts +173 -173
- package/src/hooks/significance-filter.ts +250 -250
- package/src/knowledge/claims.ts +51 -1
- package/src/knowledge/context-assembly.ts +97 -0
- package/src/knowledge/types.ts +1 -0
- package/src/knowledge/workflows.ts +34 -3
- package/src/knowledge/workset.ts +179 -10
- package/src/llm/memory-manager.ts +328 -328
- package/src/llm/provider.ts +885 -885
- package/src/llm/quality.ts +248 -248
- package/src/memory/admission.ts +57 -0
- package/src/memory/attribution-guard.ts +249 -249
- package/src/memory/auto-relations.ts +21 -0
- package/src/memory/consolidation.ts +13 -2
- package/src/memory/disclosure-policy.ts +140 -135
- package/src/memory/entity-extractor.ts +197 -197
- package/src/memory/export-import.ts +11 -3
- package/src/memory/formation/evaluate.ts +217 -217
- package/src/memory/formation/extract.ts +361 -361
- package/src/memory/formation/index.ts +417 -417
- package/src/memory/formation/resolve.ts +344 -344
- package/src/memory/formation/types.ts +315 -315
- package/src/memory/freshness.ts +122 -122
- package/src/memory/graph-context.ts +8 -2
- package/src/memory/graph-scope.ts +46 -0
- package/src/memory/graph.ts +197 -197
- package/src/memory/observations.ts +162 -4
- package/src/memory/quality-audit.ts +2 -0
- package/src/memory/refs.ts +94 -94
- package/src/memory/retention.ts +22 -2
- package/src/memory/secret-filter.ts +79 -79
- package/src/memory/session.ts +5 -2
- package/src/memory/visibility.ts +80 -0
- package/src/multimodal/image-loader.ts +143 -143
- package/src/orchestrate/adapters/claude-stream.ts +192 -192
- package/src/orchestrate/adapters/claude.ts +111 -111
- package/src/orchestrate/adapters/codex-stream.ts +134 -134
- package/src/orchestrate/adapters/codex.ts +41 -41
- package/src/orchestrate/adapters/gemini-stream.ts +166 -166
- package/src/orchestrate/adapters/gemini.ts +42 -42
- package/src/orchestrate/adapters/index.ts +73 -73
- package/src/orchestrate/adapters/opencode-stream.ts +143 -143
- package/src/orchestrate/adapters/opencode.ts +47 -47
- package/src/orchestrate/adapters/spawn-helper.ts +286 -286
- package/src/orchestrate/adapters/types.ts +77 -77
- package/src/orchestrate/capability-router.ts +284 -284
- package/src/orchestrate/context-compact.ts +188 -188
- package/src/orchestrate/cost-tracker.ts +219 -219
- package/src/orchestrate/error-recovery.ts +191 -191
- package/src/orchestrate/evidence.ts +140 -140
- package/src/orchestrate/ledger.ts +110 -110
- package/src/orchestrate/memorix-bridge.ts +378 -340
- package/src/orchestrate/output-budget.ts +80 -80
- package/src/orchestrate/permission.ts +152 -152
- package/src/orchestrate/pipeline-trace.ts +131 -131
- package/src/orchestrate/prompt-builder.ts +155 -155
- package/src/orchestrate/ring-buffer.ts +37 -37
- package/src/orchestrate/task-graph.ts +389 -389
- package/src/orchestrate/verify-gate.ts +33 -10
- package/src/orchestrate/worktree.ts +232 -232
- package/src/project/aliases.ts +374 -374
- package/src/project/detector.ts +268 -268
- package/src/rules/adapters/claude-code.ts +99 -99
- package/src/rules/adapters/codex.ts +97 -97
- package/src/rules/adapters/copilot.ts +124 -124
- package/src/rules/adapters/cursor.ts +114 -114
- package/src/rules/adapters/kiro.ts +126 -126
- package/src/rules/adapters/trae.ts +56 -56
- package/src/rules/adapters/windsurf.ts +83 -83
- package/src/rules/syncer.ts +235 -235
- package/src/runtime/control-plane-maintenance.ts +1 -0
- package/src/runtime/isolated-maintenance.ts +1 -0
- package/src/runtime/lifecycle.ts +18 -0
- package/src/runtime/maintenance-jobs.ts +1 -0
- package/src/runtime/maintenance-runner.ts +2 -0
- package/src/runtime/project-maintenance.ts +89 -0
- package/src/sdk.ts +334 -304
- package/src/search/intent-detector.ts +289 -289
- package/src/search/query-expansion.ts +52 -52
- package/src/server/formation-timeout.ts +27 -27
- package/src/server.ts +334 -93
- package/src/skills/mini-skills.ts +386 -386
- package/src/store/chat-store.ts +119 -119
- package/src/store/graph-store.ts +249 -249
- package/src/store/mini-skill-store.ts +349 -349
- package/src/store/orama-store.ts +61 -6
- package/src/store/persistence-json.ts +212 -212
- package/src/store/persistence.ts +291 -291
- package/src/store/project-affinity.ts +195 -195
- package/src/store/sqlite-db.ts +23 -1
- package/src/store/sqlite-store.ts +12 -2
- package/src/team/event-bus.ts +76 -76
- package/src/team/file-locks.ts +173 -173
- package/src/team/handoff.ts +168 -161
- package/src/team/messages.ts +203 -203
- package/src/team/poll.ts +132 -132
- package/src/team/tasks.ts +211 -211
- package/src/types.ts +51 -0
- package/src/wiki/generator.ts +2 -0
- package/src/workspace/mcp-adapters/codex.ts +191 -191
- package/src/workspace/mcp-adapters/copilot.ts +105 -105
- package/src/workspace/mcp-adapters/cursor.ts +53 -53
- package/src/workspace/mcp-adapters/kiro.ts +64 -64
- package/src/workspace/mcp-adapters/opencode.ts +123 -123
- package/src/workspace/mcp-adapters/trae.ts +134 -134
- package/src/workspace/mcp-adapters/windsurf.ts +91 -91
- package/src/workspace/sanitizer.ts +60 -60
- package/src/workspace/workflow-sync.ts +131 -131
|
@@ -1,123 +1,128 @@
|
|
|
1
|
-
import { defineCommand } from 'citty';
|
|
1
|
+
import { defineCommand } from 'citty';
|
|
2
2
|
import type { AgentTarget } from '../../types.js';
|
|
3
3
|
import { getObservationStore } from '../../store/obs-store.js';
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const {
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
skills
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
4
|
+
import { filterReadableObservations, resolveObservationVisibility } from '../../memory/visibility.js';
|
|
5
|
+
import { emitError, emitResult, getCliProjectContext } from './operator-shared.js';
|
|
6
|
+
|
|
7
|
+
export default defineCommand({
|
|
8
|
+
meta: {
|
|
9
|
+
name: 'skills',
|
|
10
|
+
description: 'Discover, generate, and inspect project skills from the CLI',
|
|
11
|
+
},
|
|
12
|
+
args: {
|
|
13
|
+
name: { type: 'string', description: 'Skill name for "show"' },
|
|
14
|
+
target: { type: 'string', description: 'Target agent for generated skills' },
|
|
15
|
+
write: { type: 'boolean', description: 'Write generated skills to disk' },
|
|
16
|
+
json: { type: 'boolean', description: 'Emit machine-readable JSON output' },
|
|
17
|
+
},
|
|
18
|
+
run: async ({ args }) => {
|
|
19
|
+
const action = (args._ as string[])?.[0] || '';
|
|
20
|
+
const asJson = !!args.json;
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
const { project, reader } = await getCliProjectContext();
|
|
24
|
+
const { SkillsEngine } = await import('../../skills/engine.js');
|
|
25
|
+
const engine = new SkillsEngine(project.rootPath);
|
|
26
|
+
|
|
27
|
+
switch (action) {
|
|
28
|
+
case 'list': {
|
|
29
|
+
const skills = engine.listSkills();
|
|
30
|
+
emitResult(
|
|
31
|
+
{ project, skills },
|
|
32
|
+
skills.length === 0
|
|
33
|
+
? 'No skills found in project or global agent directories.'
|
|
34
|
+
: skills.map((skill) => `- ${skill.name} (${skill.sourceAgent})`).join('\n'),
|
|
35
|
+
asJson,
|
|
36
|
+
);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
case 'generate': {
|
|
41
|
+
const observations = filterReadableObservations(
|
|
42
|
+
await getObservationStore().loadAll(),
|
|
43
|
+
reader,
|
|
44
|
+
).filter((observation) => resolveObservationVisibility(observation) === 'project')
|
|
42
45
|
.map((obs) => ({
|
|
43
|
-
id: obs.id,
|
|
44
|
-
entityName: obs.entityName,
|
|
45
|
-
type: obs.type,
|
|
46
|
-
title: obs.title,
|
|
47
|
-
narrative: obs.narrative,
|
|
48
|
-
facts: obs.facts,
|
|
49
|
-
concepts: obs.concepts,
|
|
50
|
-
filesModified: obs.filesModified,
|
|
51
|
-
createdAt: obs.createdAt,
|
|
52
|
-
status: obs.status,
|
|
53
|
-
source: obs.source,
|
|
54
|
-
}));
|
|
55
|
-
const skills = engine.generateFromObservations(observations);
|
|
56
|
-
const written: string[] = [];
|
|
57
|
-
if (args.write && args.target) {
|
|
58
|
-
for (const skill of skills) {
|
|
59
|
-
const writtenPath = engine.writeSkill(skill, args.target as AgentTarget);
|
|
60
|
-
if (writtenPath) written.push(writtenPath);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
emitResult(
|
|
64
|
-
{ project, skills, written },
|
|
65
|
-
skills.length === 0
|
|
66
|
-
? 'No skill-worthy patterns found yet.'
|
|
67
|
-
: skills.map((skill) => `- ${skill.name}: ${skill.description}`).join('\n'),
|
|
68
|
-
asJson,
|
|
69
|
-
);
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
case 'show': {
|
|
74
|
-
const name = (args.name as string | undefined)?.trim();
|
|
75
|
-
if (!name) {
|
|
76
|
-
emitError('name is required for "memorix skills show"', asJson);
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
const direct = engine.injectSkill(name);
|
|
80
|
-
if (direct) {
|
|
81
|
-
emitResult({ project, skill: direct }, direct.content, asJson);
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const observations = (
|
|
86
|
-
|
|
46
|
+
id: obs.id,
|
|
47
|
+
entityName: obs.entityName,
|
|
48
|
+
type: obs.type,
|
|
49
|
+
title: obs.title,
|
|
50
|
+
narrative: obs.narrative,
|
|
51
|
+
facts: obs.facts,
|
|
52
|
+
concepts: obs.concepts,
|
|
53
|
+
filesModified: obs.filesModified,
|
|
54
|
+
createdAt: obs.createdAt,
|
|
55
|
+
status: obs.status,
|
|
56
|
+
source: obs.source,
|
|
57
|
+
}));
|
|
58
|
+
const skills = engine.generateFromObservations(observations);
|
|
59
|
+
const written: string[] = [];
|
|
60
|
+
if (args.write && args.target) {
|
|
61
|
+
for (const skill of skills) {
|
|
62
|
+
const writtenPath = engine.writeSkill(skill, args.target as AgentTarget);
|
|
63
|
+
if (writtenPath) written.push(writtenPath);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
emitResult(
|
|
67
|
+
{ project, skills, written },
|
|
68
|
+
skills.length === 0
|
|
69
|
+
? 'No skill-worthy patterns found yet.'
|
|
70
|
+
: skills.map((skill) => `- ${skill.name}: ${skill.description}`).join('\n'),
|
|
71
|
+
asJson,
|
|
72
|
+
);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
case 'show': {
|
|
77
|
+
const name = (args.name as string | undefined)?.trim();
|
|
78
|
+
if (!name) {
|
|
79
|
+
emitError('name is required for "memorix skills show"', asJson);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const direct = engine.injectSkill(name);
|
|
83
|
+
if (direct) {
|
|
84
|
+
emitResult({ project, skill: direct }, direct.content, asJson);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const observations = filterReadableObservations(
|
|
89
|
+
await getObservationStore().loadAll(),
|
|
90
|
+
reader,
|
|
91
|
+
).filter((observation) => resolveObservationVisibility(observation) === 'project')
|
|
87
92
|
.map((obs) => ({
|
|
88
|
-
id: obs.id,
|
|
89
|
-
entityName: obs.entityName,
|
|
90
|
-
type: obs.type,
|
|
91
|
-
title: obs.title,
|
|
92
|
-
narrative: obs.narrative,
|
|
93
|
-
facts: obs.facts,
|
|
94
|
-
concepts: obs.concepts,
|
|
95
|
-
filesModified: obs.filesModified,
|
|
96
|
-
createdAt: obs.createdAt,
|
|
97
|
-
status: obs.status,
|
|
98
|
-
source: obs.source,
|
|
99
|
-
}));
|
|
100
|
-
const generated = engine.generateFromObservations(observations);
|
|
101
|
-
const skill = generated.find((entry) => entry.name.toLowerCase() === name.toLowerCase());
|
|
102
|
-
if (!skill) {
|
|
103
|
-
emitError(`Skill "${name}" not found`, asJson);
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
emitResult({ project, skill }, skill.content, asJson);
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
default:
|
|
111
|
-
console.log('Memorix Skills Commands');
|
|
112
|
-
console.log('');
|
|
113
|
-
console.log('Usage:');
|
|
114
|
-
console.log(' memorix skills list');
|
|
115
|
-
console.log(' memorix skills generate [--write --target codex]');
|
|
116
|
-
console.log(' memorix skills show --name <skill>');
|
|
117
|
-
}
|
|
118
|
-
} catch (error) {
|
|
119
|
-
emitError(error instanceof Error ? error.message : String(error), asJson);
|
|
120
|
-
}
|
|
121
|
-
},
|
|
122
|
-
});
|
|
123
|
-
|
|
93
|
+
id: obs.id,
|
|
94
|
+
entityName: obs.entityName,
|
|
95
|
+
type: obs.type,
|
|
96
|
+
title: obs.title,
|
|
97
|
+
narrative: obs.narrative,
|
|
98
|
+
facts: obs.facts,
|
|
99
|
+
concepts: obs.concepts,
|
|
100
|
+
filesModified: obs.filesModified,
|
|
101
|
+
createdAt: obs.createdAt,
|
|
102
|
+
status: obs.status,
|
|
103
|
+
source: obs.source,
|
|
104
|
+
}));
|
|
105
|
+
const generated = engine.generateFromObservations(observations);
|
|
106
|
+
const skill = generated.find((entry) => entry.name.toLowerCase() === name.toLowerCase());
|
|
107
|
+
if (!skill) {
|
|
108
|
+
emitError(`Skill "${name}" not found`, asJson);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
emitResult({ project, skill }, skill.content, asJson);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
default:
|
|
116
|
+
console.log('Memorix Skills Commands');
|
|
117
|
+
console.log('');
|
|
118
|
+
console.log('Usage:');
|
|
119
|
+
console.log(' memorix skills list');
|
|
120
|
+
console.log(' memorix skills generate [--write --target codex]');
|
|
121
|
+
console.log(' memorix skills show --name <skill>');
|
|
122
|
+
}
|
|
123
|
+
} catch (error) {
|
|
124
|
+
emitError(error instanceof Error ? error.message : String(error), asJson);
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
|
|
@@ -39,8 +39,8 @@ export default defineCommand({
|
|
|
39
39
|
const { initObservationStore, getObservationStore } = await import('../../store/obs-store.js');
|
|
40
40
|
await initObservationStore(dataDir);
|
|
41
41
|
const store = getObservationStore();
|
|
42
|
-
const
|
|
43
|
-
const projectObs =
|
|
42
|
+
const { filterReadableObservations } = await import('../../memory/visibility.js');
|
|
43
|
+
const projectObs = filterReadableObservations(await store.loadAll(), { projectId: project.id });
|
|
44
44
|
obsCount = projectObs.length;
|
|
45
45
|
activeCount = projectObs.filter(o => (o.status ?? 'active') === 'active').length;
|
|
46
46
|
} catch { /* ignore */ }
|
|
@@ -165,7 +165,8 @@ export default defineCommand({
|
|
|
165
165
|
const { initObservationStore, getObservationStore } = await import('../../store/obs-store.js');
|
|
166
166
|
await initObservationStore(dataDir);
|
|
167
167
|
const store = getObservationStore();
|
|
168
|
-
const
|
|
168
|
+
const { filterReadableObservations } = await import('../../memory/visibility.js');
|
|
169
|
+
const allObs = filterReadableObservations(await store.loadAll(), { projectId: project.id });
|
|
169
170
|
const gitCount = allObs.filter(o => o.source === 'git').length;
|
|
170
171
|
const reasoningCount = allObs.filter(o => o.type === 'reasoning').length;
|
|
171
172
|
if (gitCount > 0 || reasoningCount > 0) {
|