pikakit 3.0.5 → 3.7.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/README.md +1 -1
- package/bin/lib/commands/install.js +119 -242
- package/package.json +3 -4
- package/lib/agent-cli/bin/agent.js +0 -187
- package/lib/agent-cli/dashboard/dashboard_server.js +0 -312
- package/lib/agent-cli/lib/ab-testing.js +0 -364
- package/lib/agent-cli/lib/audit.js +0 -154
- package/lib/agent-cli/lib/audit.test.js +0 -100
- package/lib/agent-cli/lib/auto-learn.js +0 -319
- package/lib/agent-cli/lib/backup.js +0 -138
- package/lib/agent-cli/lib/backup.test.js +0 -78
- package/lib/agent-cli/lib/causality-engine.js +0 -331
- package/lib/agent-cli/lib/cognitive-lesson.js +0 -476
- package/lib/agent-cli/lib/completion.js +0 -149
- package/lib/agent-cli/lib/config.js +0 -35
- package/lib/agent-cli/lib/dashboard-data.js +0 -380
- package/lib/agent-cli/lib/eslint-fix.js +0 -238
- package/lib/agent-cli/lib/evolution-signal.js +0 -215
- package/lib/agent-cli/lib/export.js +0 -86
- package/lib/agent-cli/lib/export.test.js +0 -65
- package/lib/agent-cli/lib/fix.js +0 -337
- package/lib/agent-cli/lib/fix.test.js +0 -80
- package/lib/agent-cli/lib/gemini-export.js +0 -83
- package/lib/agent-cli/lib/generate-registry.js +0 -42
- package/lib/agent-cli/lib/hooks/install-hooks.js +0 -152
- package/lib/agent-cli/lib/hooks/lint-learn.js +0 -172
- package/lib/agent-cli/lib/icons.js +0 -93
- package/lib/agent-cli/lib/ignore.js +0 -116
- package/lib/agent-cli/lib/ignore.test.js +0 -58
- package/lib/agent-cli/lib/init.js +0 -124
- package/lib/agent-cli/lib/knowledge-index.js +0 -326
- package/lib/agent-cli/lib/knowledge-metrics.js +0 -335
- package/lib/agent-cli/lib/knowledge-retention.js +0 -398
- package/lib/agent-cli/lib/knowledge-validator.js +0 -312
- package/lib/agent-cli/lib/learn.js +0 -255
- package/lib/agent-cli/lib/learn.test.js +0 -70
- package/lib/agent-cli/lib/metrics-collector.js +0 -410
- package/lib/agent-cli/lib/proposals.js +0 -199
- package/lib/agent-cli/lib/proposals.test.js +0 -56
- package/lib/agent-cli/lib/recall.js +0 -835
- package/lib/agent-cli/lib/recall.test.js +0 -107
- package/lib/agent-cli/lib/reinforcement.js +0 -299
- package/lib/agent-cli/lib/selfevolution-bridge.js +0 -167
- package/lib/agent-cli/lib/settings.js +0 -203
- package/lib/agent-cli/lib/skill-generator.js +0 -379
- package/lib/agent-cli/lib/skill-learn.js +0 -296
- package/lib/agent-cli/lib/stats.js +0 -132
- package/lib/agent-cli/lib/stats.test.js +0 -94
- package/lib/agent-cli/lib/types.js +0 -33
- package/lib/agent-cli/lib/ui/audit-ui.js +0 -146
- package/lib/agent-cli/lib/ui/backup-ui.js +0 -107
- package/lib/agent-cli/lib/ui/clack-helpers.js +0 -317
- package/lib/agent-cli/lib/ui/common.js +0 -83
- package/lib/agent-cli/lib/ui/completion-ui.js +0 -126
- package/lib/agent-cli/lib/ui/custom-select.js +0 -69
- package/lib/agent-cli/lib/ui/dashboard-ui.js +0 -222
- package/lib/agent-cli/lib/ui/evolution-signals-ui.js +0 -107
- package/lib/agent-cli/lib/ui/export-ui.js +0 -94
- package/lib/agent-cli/lib/ui/fix-all-ui.js +0 -191
- package/lib/agent-cli/lib/ui/help-ui.js +0 -49
- package/lib/agent-cli/lib/ui/index.js +0 -199
- package/lib/agent-cli/lib/ui/init-ui.js +0 -56
- package/lib/agent-cli/lib/ui/knowledge-ui.js +0 -55
- package/lib/agent-cli/lib/ui/learn-ui.js +0 -706
- package/lib/agent-cli/lib/ui/lessons-ui.js +0 -148
- package/lib/agent-cli/lib/ui/pretty.js +0 -145
- package/lib/agent-cli/lib/ui/proposals-ui.js +0 -99
- package/lib/agent-cli/lib/ui/recall-ui.js +0 -342
- package/lib/agent-cli/lib/ui/routing-demo.js +0 -79
- package/lib/agent-cli/lib/ui/routing-ui.js +0 -325
- package/lib/agent-cli/lib/ui/settings-ui.js +0 -381
- package/lib/agent-cli/lib/ui/stats-ui.js +0 -123
- package/lib/agent-cli/lib/ui/watch-ui.js +0 -236
- package/lib/agent-cli/lib/watcher.js +0 -181
- package/lib/agent-cli/lib/watcher.test.js +0 -85
- package/lib/agent-cli/src/MIGRATION.md +0 -418
- package/lib/agent-cli/src/README.md +0 -367
- package/lib/agent-cli/src/core/evolution/evolution-signal.js +0 -42
- package/lib/agent-cli/src/core/evolution/index.js +0 -17
- package/lib/agent-cli/src/core/evolution/review-gate.js +0 -40
- package/lib/agent-cli/src/core/evolution/signal-detector.js +0 -137
- package/lib/agent-cli/src/core/evolution/signal-queue.js +0 -79
- package/lib/agent-cli/src/core/evolution/threshold-checker.js +0 -79
- package/lib/agent-cli/src/core/index.js +0 -15
- package/lib/agent-cli/src/core/learning/cognitive-enhancer.js +0 -282
- package/lib/agent-cli/src/core/learning/index.js +0 -12
- package/lib/agent-cli/src/core/learning/lesson-synthesizer.js +0 -83
- package/lib/agent-cli/src/core/scanning/index.js +0 -14
- package/lib/agent-cli/src/data/index.js +0 -13
- package/lib/agent-cli/src/data/repositories/index.js +0 -8
- package/lib/agent-cli/src/data/repositories/lesson-repository.js +0 -130
- package/lib/agent-cli/src/data/repositories/signal-repository.js +0 -119
- package/lib/agent-cli/src/data/storage/index.js +0 -8
- package/lib/agent-cli/src/data/storage/json-storage.js +0 -64
- package/lib/agent-cli/src/data/storage/yaml-storage.js +0 -66
- package/lib/agent-cli/src/infrastructure/index.js +0 -13
- package/lib/agent-cli/src/presentation/formatters/skill-formatter.js +0 -232
- package/lib/agent-cli/src/services/export-service.js +0 -162
- package/lib/agent-cli/src/services/index.js +0 -13
- package/lib/agent-cli/src/services/learning-service.js +0 -99
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Ignore patterns parser for PikaKit
|
|
3
|
-
* Supports .agentignore file with glob patterns
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import fs from "fs";
|
|
7
|
-
import path from "path";
|
|
8
|
-
import { cwd } from "./config.js";
|
|
9
|
-
|
|
10
|
-
/** Default patterns always ignored */
|
|
11
|
-
const DEFAULT_IGNORES = [
|
|
12
|
-
"node_modules/**",
|
|
13
|
-
".git/**",
|
|
14
|
-
"dist/**",
|
|
15
|
-
"build/**",
|
|
16
|
-
".next/**",
|
|
17
|
-
"coverage/**",
|
|
18
|
-
"*.log"
|
|
19
|
-
];
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Load ignore patterns from .agentignore file
|
|
23
|
-
* @param {string} basePath - Base directory
|
|
24
|
-
* @returns {string[]} Array of patterns
|
|
25
|
-
*/
|
|
26
|
-
export function loadIgnorePatterns(basePath = cwd) {
|
|
27
|
-
const patterns = [...DEFAULT_IGNORES];
|
|
28
|
-
const ignoreFile = path.join(basePath, ".agentignore");
|
|
29
|
-
|
|
30
|
-
try {
|
|
31
|
-
if (fs.existsSync(ignoreFile)) {
|
|
32
|
-
const content = fs.readFileSync(ignoreFile, "utf8");
|
|
33
|
-
const lines = content.split("\n");
|
|
34
|
-
|
|
35
|
-
for (const line of lines) {
|
|
36
|
-
const trimmed = line.trim();
|
|
37
|
-
// Skip empty lines and comments
|
|
38
|
-
if (trimmed && !trimmed.startsWith("#")) {
|
|
39
|
-
patterns.push(trimmed);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
} catch (e) {
|
|
44
|
-
// Silently use defaults
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return [...new Set(patterns)]; // Remove duplicates
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Check if a file path matches any ignore pattern
|
|
52
|
-
* @param {string} filePath - Relative file path
|
|
53
|
-
* @param {string[]} patterns - Ignore patterns
|
|
54
|
-
* @returns {boolean}
|
|
55
|
-
*/
|
|
56
|
-
export function isIgnored(filePath, patterns) {
|
|
57
|
-
const normalized = filePath.replace(/\\/g, "/");
|
|
58
|
-
|
|
59
|
-
for (const pattern of patterns) {
|
|
60
|
-
// Simple glob matching
|
|
61
|
-
const regex = patternToRegex(pattern);
|
|
62
|
-
if (regex.test(normalized)) {
|
|
63
|
-
return true;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
return false;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Convert glob pattern to regex
|
|
72
|
-
* @param {string} pattern - Glob pattern
|
|
73
|
-
* @returns {RegExp}
|
|
74
|
-
*/
|
|
75
|
-
function patternToRegex(pattern) {
|
|
76
|
-
let regex = pattern
|
|
77
|
-
.replace(/\./g, "\\.") // Escape dots
|
|
78
|
-
.replace(/\*\*/g, ".*") // ** matches everything
|
|
79
|
-
.replace(/\*/g, "[^/]*") // * matches segment
|
|
80
|
-
.replace(/\?/g, "."); // ? matches single char
|
|
81
|
-
|
|
82
|
-
// Handle directory patterns (ending with /)
|
|
83
|
-
if (pattern.endsWith("/")) {
|
|
84
|
-
regex = regex.slice(0, -1) + "(/.*)?";
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
return new RegExp(`^${regex}$|/${regex}$|^${regex}/`);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Filter files array by ignore patterns
|
|
92
|
-
* @param {string[]} files - Array of file paths
|
|
93
|
-
* @param {string[]} patterns - Ignore patterns
|
|
94
|
-
* @returns {{ included: string[], ignored: number }}
|
|
95
|
-
*/
|
|
96
|
-
export function filterFiles(files, patterns) {
|
|
97
|
-
const included = [];
|
|
98
|
-
let ignored = 0;
|
|
99
|
-
|
|
100
|
-
for (const file of files) {
|
|
101
|
-
if (isIgnored(file, patterns)) {
|
|
102
|
-
ignored++;
|
|
103
|
-
} else {
|
|
104
|
-
included.push(file);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
return { included, ignored };
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export default {
|
|
112
|
-
loadIgnorePatterns,
|
|
113
|
-
isIgnored,
|
|
114
|
-
filterFiles,
|
|
115
|
-
DEFAULT_IGNORES
|
|
116
|
-
};
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Tests for ignore module
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
6
|
-
import fs from "fs";
|
|
7
|
-
import path from "path";
|
|
8
|
-
import os from "os";
|
|
9
|
-
|
|
10
|
-
// Import directly without mocking - test actual behavior
|
|
11
|
-
import { loadIgnorePatterns, isIgnored } from "./ignore.js";
|
|
12
|
-
|
|
13
|
-
describe("ignore", () => {
|
|
14
|
-
describe("isIgnored", () => {
|
|
15
|
-
it("returns false when no patterns", () => {
|
|
16
|
-
const result = isIgnored("some/path.js", []);
|
|
17
|
-
expect(result).toBe(false);
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it("matches exact pattern", () => {
|
|
21
|
-
const result = isIgnored("node_modules/pkg/file.js", ["node_modules"]);
|
|
22
|
-
expect(result).toBe(true);
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it("matches glob pattern", () => {
|
|
26
|
-
const result = isIgnored("debug.log", ["*.log"]);
|
|
27
|
-
expect(result).toBe(true);
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it("returns false for non-matching path", () => {
|
|
31
|
-
const result = isIgnored("src/app.js", ["*.log", "node_modules"]);
|
|
32
|
-
expect(result).toBe(false);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it("matches directory pattern", () => {
|
|
36
|
-
const result = isIgnored("dist/bundle.js", ["dist/**"]);
|
|
37
|
-
expect(result).toBe(true);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it("matches nested paths", () => {
|
|
41
|
-
const result = isIgnored(".git/objects/pack", [".git/**"]);
|
|
42
|
-
expect(result).toBe(true);
|
|
43
|
-
});
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
describe("loadIgnorePatterns", () => {
|
|
47
|
-
it("returns array of patterns", () => {
|
|
48
|
-
const patterns = loadIgnorePatterns();
|
|
49
|
-
expect(Array.isArray(patterns)).toBe(true);
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it("includes default patterns", () => {
|
|
53
|
-
const patterns = loadIgnorePatterns();
|
|
54
|
-
// Should have at least some patterns (defaults or from file)
|
|
55
|
-
expect(patterns.length).toBeGreaterThan(0);
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
});
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Project initialization wizard
|
|
3
|
-
* Sets up agent config for new projects
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import fs from "fs";
|
|
7
|
-
import path from "path";
|
|
8
|
-
import { cwd, AGENT_DIR, KNOWLEDGE_DIR, LESSONS_PATH } from "./config.js";
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Detect project type based on files
|
|
12
|
-
* @returns {string} Project type
|
|
13
|
-
*/
|
|
14
|
-
export function detectProjectType() {
|
|
15
|
-
const files = fs.readdirSync(cwd);
|
|
16
|
-
|
|
17
|
-
if (files.includes("package.json")) {
|
|
18
|
-
const pkg = JSON.parse(fs.readFileSync(path.join(cwd, "package.json"), "utf8"));
|
|
19
|
-
|
|
20
|
-
if (pkg.dependencies?.next || pkg.devDependencies?.next) return "nextjs";
|
|
21
|
-
if (pkg.dependencies?.react || pkg.devDependencies?.react) return "react";
|
|
22
|
-
if (pkg.dependencies?.vue || pkg.devDependencies?.vue) return "vue";
|
|
23
|
-
if (pkg.dependencies?.express || pkg.devDependencies?.express) return "node";
|
|
24
|
-
return "node";
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
if (files.includes("requirements.txt") || files.includes("pyproject.toml")) return "python";
|
|
28
|
-
if (files.includes("Cargo.toml")) return "rust";
|
|
29
|
-
if (files.includes("go.mod")) return "go";
|
|
30
|
-
if (files.includes("pom.xml") || files.includes("build.gradle")) return "java";
|
|
31
|
-
|
|
32
|
-
return "generic";
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Get default ignore patterns for project type
|
|
37
|
-
* @param {string} projectType
|
|
38
|
-
* @returns {string[]}
|
|
39
|
-
*/
|
|
40
|
-
export function getDefaultIgnorePatterns(projectType) {
|
|
41
|
-
const common = [
|
|
42
|
-
"node_modules/**",
|
|
43
|
-
".git/**",
|
|
44
|
-
"dist/**",
|
|
45
|
-
"build/**",
|
|
46
|
-
"coverage/**",
|
|
47
|
-
"*.log",
|
|
48
|
-
"*.lock",
|
|
49
|
-
".env*"
|
|
50
|
-
];
|
|
51
|
-
|
|
52
|
-
const specific = {
|
|
53
|
-
node: ["package-lock.json"],
|
|
54
|
-
nextjs: [".next/**", "out/**"],
|
|
55
|
-
react: ["build/**"],
|
|
56
|
-
vue: [".nuxt/**"],
|
|
57
|
-
python: ["__pycache__/**", "*.pyc", ".venv/**", "venv/**"],
|
|
58
|
-
rust: ["target/**"],
|
|
59
|
-
go: ["vendor/**"],
|
|
60
|
-
java: ["target/**", "*.class"],
|
|
61
|
-
generic: []
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
return [...common, ...(specific[projectType] || [])];
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Initialize agent config
|
|
69
|
-
* @param {object} options
|
|
70
|
-
* @param {boolean} options.force - Overwrite existing
|
|
71
|
-
* @returns {{ success: boolean, message: string }}
|
|
72
|
-
*/
|
|
73
|
-
export function initProject(options = {}) {
|
|
74
|
-
const { force = false } = options;
|
|
75
|
-
|
|
76
|
-
// Check if already initialized
|
|
77
|
-
if (fs.existsSync(KNOWLEDGE_DIR) && !force) {
|
|
78
|
-
return {
|
|
79
|
-
success: false,
|
|
80
|
-
message: "Already initialized. Use --force to reinitialize."
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Detect project type
|
|
85
|
-
const projectType = detectProjectType();
|
|
86
|
-
|
|
87
|
-
// Create directories
|
|
88
|
-
fs.mkdirSync(KNOWLEDGE_DIR, { recursive: true });
|
|
89
|
-
|
|
90
|
-
// Create lessons-learned.yaml
|
|
91
|
-
if (!fs.existsSync(LESSONS_PATH)) {
|
|
92
|
-
const initialLessons = `# PikaKit - Lessons Learned
|
|
93
|
-
# Project Type: ${projectType}
|
|
94
|
-
# Created: ${new Date().toISOString()}
|
|
95
|
-
|
|
96
|
-
lessons: []
|
|
97
|
-
`;
|
|
98
|
-
fs.writeFileSync(LESSONS_PATH, initialLessons);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// Create .agentignore if not exists
|
|
102
|
-
const agentignorePath = path.join(cwd, ".agentignore");
|
|
103
|
-
if (!fs.existsSync(agentignorePath)) {
|
|
104
|
-
const patterns = getDefaultIgnorePatterns(projectType);
|
|
105
|
-
fs.writeFileSync(agentignorePath, patterns.join("\n") + "\n");
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
return {
|
|
109
|
-
success: true,
|
|
110
|
-
message: `Initialized for ${projectType} project`,
|
|
111
|
-
projectType,
|
|
112
|
-
paths: {
|
|
113
|
-
knowledge: KNOWLEDGE_DIR,
|
|
114
|
-
lessons: LESSONS_PATH,
|
|
115
|
-
agentignore: agentignorePath
|
|
116
|
-
}
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
export default {
|
|
121
|
-
detectProjectType,
|
|
122
|
-
getDefaultIgnorePatterns,
|
|
123
|
-
initProject
|
|
124
|
-
};
|
|
@@ -1,326 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Knowledge Index Generator
|
|
4
|
-
*
|
|
5
|
-
* Builds inverted index for O(1) pattern lookup in knowledge base.
|
|
6
|
-
* Regenerates on-demand when knowledge files change.
|
|
7
|
-
*
|
|
8
|
-
* Usage:
|
|
9
|
-
* agent index --rebuild # Force rebuild index
|
|
10
|
-
* agent index --status # Check index freshness
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import fs from 'fs';
|
|
14
|
-
import path from 'path';
|
|
15
|
-
import yaml from 'js-yaml';
|
|
16
|
-
import { KNOWLEDGE_DIR } from './config.js';
|
|
17
|
-
|
|
18
|
-
const INDEX_PATH = path.join(KNOWLEDGE_DIR, 'index.json');
|
|
19
|
-
const INDEX_VERSION = 1;
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Load all lessons from knowledge base
|
|
23
|
-
* @returns {{ mistakes: Array, improvements: Array }}
|
|
24
|
-
*/
|
|
25
|
-
function loadAllLessons() {
|
|
26
|
-
const mistakes = [];
|
|
27
|
-
const improvements = [];
|
|
28
|
-
|
|
29
|
-
// Load mistakes.yaml
|
|
30
|
-
const mistakesPath = path.join(KNOWLEDGE_DIR, 'mistakes.yaml');
|
|
31
|
-
if (fs.existsSync(mistakesPath)) {
|
|
32
|
-
const data = yaml.load(fs.readFileSync(mistakesPath, 'utf8'));
|
|
33
|
-
if (data?.mistakes) {
|
|
34
|
-
mistakes.push(...data.mistakes.map(m => ({ ...m, type: 'mistake' })));
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// Load improvements.yaml
|
|
39
|
-
const improvementsPath = path.join(KNOWLEDGE_DIR, 'improvements.yaml');
|
|
40
|
-
if (fs.existsSync(improvementsPath)) {
|
|
41
|
-
const data = yaml.load(fs.readFileSync(improvementsPath, 'utf8'));
|
|
42
|
-
if (data?.improvements) {
|
|
43
|
-
improvements.push(...data.improvements.map(i => ({ ...i, type: 'improvement' })));
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return { mistakes, improvements };
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Build inverted index from lessons
|
|
52
|
-
* @param {Array} lessons - All lessons combined
|
|
53
|
-
* @returns {Object} Index structure
|
|
54
|
-
*/
|
|
55
|
-
function buildIndex(lessons) {
|
|
56
|
-
const patternIndex = {}; // pattern word -> lesson IDs
|
|
57
|
-
const tagIndex = {}; // tag -> lesson IDs
|
|
58
|
-
const idIndex = {}; // id -> lesson (for direct lookup)
|
|
59
|
-
const severityIndex = { // severity -> lesson IDs
|
|
60
|
-
ERROR: [],
|
|
61
|
-
WARNING: [],
|
|
62
|
-
INFO: []
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
for (const lesson of lessons) {
|
|
66
|
-
const id = lesson.id;
|
|
67
|
-
|
|
68
|
-
// ID index (direct lookup)
|
|
69
|
-
idIndex[id] = {
|
|
70
|
-
pattern: lesson.pattern,
|
|
71
|
-
message: lesson.message,
|
|
72
|
-
severity: lesson.severity || 'WARNING',
|
|
73
|
-
type: lesson.type,
|
|
74
|
-
hitCount: lesson.hitCount || 0,
|
|
75
|
-
confidence: lesson.cognitive?.confidence || 0.3
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
// Pattern index (tokenize pattern for partial matching)
|
|
79
|
-
if (lesson.pattern) {
|
|
80
|
-
const tokens = tokenizePattern(lesson.pattern);
|
|
81
|
-
for (const token of tokens) {
|
|
82
|
-
if (!patternIndex[token]) {
|
|
83
|
-
patternIndex[token] = [];
|
|
84
|
-
}
|
|
85
|
-
if (!patternIndex[token].includes(id)) {
|
|
86
|
-
patternIndex[token].push(id);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// Tag index
|
|
92
|
-
if (lesson.tags && Array.isArray(lesson.tags)) {
|
|
93
|
-
for (const tag of lesson.tags) {
|
|
94
|
-
if (!tagIndex[tag]) {
|
|
95
|
-
tagIndex[tag] = [];
|
|
96
|
-
}
|
|
97
|
-
if (!tagIndex[tag].includes(id)) {
|
|
98
|
-
tagIndex[tag].push(id);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// Severity index
|
|
104
|
-
const severity = lesson.severity || 'WARNING';
|
|
105
|
-
if (severityIndex[severity]) {
|
|
106
|
-
severityIndex[severity].push(id);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
return {
|
|
111
|
-
version: INDEX_VERSION,
|
|
112
|
-
generatedAt: new Date().toISOString(),
|
|
113
|
-
stats: {
|
|
114
|
-
totalLessons: lessons.length,
|
|
115
|
-
totalMistakes: lessons.filter(l => l.type === 'mistake').length,
|
|
116
|
-
totalImprovements: lessons.filter(l => l.type === 'improvement').length,
|
|
117
|
-
totalPatterns: Object.keys(patternIndex).length,
|
|
118
|
-
totalTags: Object.keys(tagIndex).length
|
|
119
|
-
},
|
|
120
|
-
patternIndex,
|
|
121
|
-
tagIndex,
|
|
122
|
-
severityIndex,
|
|
123
|
-
idIndex
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Tokenize a regex pattern into searchable words
|
|
129
|
-
* @param {string} pattern - Regex pattern
|
|
130
|
-
* @returns {string[]} Tokens
|
|
131
|
-
*/
|
|
132
|
-
function tokenizePattern(pattern) {
|
|
133
|
-
// Extract alphanumeric words, ignoring regex special chars
|
|
134
|
-
const words = pattern
|
|
135
|
-
.replace(/[\[\]\(\)\{\}\.\*\+\?\^\$\\|]/g, ' ')
|
|
136
|
-
.split(/\s+/)
|
|
137
|
-
.map(w => w.toLowerCase().trim())
|
|
138
|
-
.filter(w => w.length >= 2);
|
|
139
|
-
|
|
140
|
-
return [...new Set(words)];
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Get modification time of knowledge files
|
|
145
|
-
* @returns {number} Most recent mtime
|
|
146
|
-
*/
|
|
147
|
-
function getKnowledgeMtime() {
|
|
148
|
-
const files = ['mistakes.yaml', 'improvements.yaml', 'lessons-learned.yaml'];
|
|
149
|
-
let maxMtime = 0;
|
|
150
|
-
|
|
151
|
-
for (const file of files) {
|
|
152
|
-
const filepath = path.join(KNOWLEDGE_DIR, file);
|
|
153
|
-
if (fs.existsSync(filepath)) {
|
|
154
|
-
const stat = fs.statSync(filepath);
|
|
155
|
-
if (stat.mtime.getTime() > maxMtime) {
|
|
156
|
-
maxMtime = stat.mtime.getTime();
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
return maxMtime;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Check if index is stale
|
|
166
|
-
* @returns {{ stale: boolean, reason?: string }}
|
|
167
|
-
*/
|
|
168
|
-
export function checkIndexFreshness() {
|
|
169
|
-
if (!fs.existsSync(INDEX_PATH)) {
|
|
170
|
-
return { stale: true, reason: 'Index does not exist' };
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
try {
|
|
174
|
-
const index = JSON.parse(fs.readFileSync(INDEX_PATH, 'utf8'));
|
|
175
|
-
const indexTime = new Date(index.generatedAt).getTime();
|
|
176
|
-
const knowledgeTime = getKnowledgeMtime();
|
|
177
|
-
|
|
178
|
-
if (knowledgeTime > indexTime) {
|
|
179
|
-
return { stale: true, reason: 'Knowledge files modified after index' };
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
if (index.version !== INDEX_VERSION) {
|
|
183
|
-
return { stale: true, reason: `Index version mismatch (${index.version} vs ${INDEX_VERSION})` };
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
return { stale: false };
|
|
187
|
-
} catch (e) {
|
|
188
|
-
return { stale: true, reason: `Index corrupted: ${e.message}` };
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
/**
|
|
193
|
-
* Load or rebuild index
|
|
194
|
-
* @param {boolean} forceRebuild - Force rebuild even if fresh
|
|
195
|
-
* @returns {Object} Index
|
|
196
|
-
*/
|
|
197
|
-
export function loadIndex(forceRebuild = false) {
|
|
198
|
-
const freshness = checkIndexFreshness();
|
|
199
|
-
|
|
200
|
-
if (!forceRebuild && !freshness.stale) {
|
|
201
|
-
// Load existing index
|
|
202
|
-
return JSON.parse(fs.readFileSync(INDEX_PATH, 'utf8'));
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
// Rebuild index
|
|
206
|
-
return rebuildIndex();
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
/**
|
|
210
|
-
* Force rebuild index
|
|
211
|
-
* @returns {Object} New index
|
|
212
|
-
*/
|
|
213
|
-
export function rebuildIndex() {
|
|
214
|
-
const { mistakes, improvements } = loadAllLessons();
|
|
215
|
-
const allLessons = [...mistakes, ...improvements];
|
|
216
|
-
|
|
217
|
-
const index = buildIndex(allLessons);
|
|
218
|
-
|
|
219
|
-
// Save index
|
|
220
|
-
fs.mkdirSync(KNOWLEDGE_DIR, { recursive: true });
|
|
221
|
-
fs.writeFileSync(INDEX_PATH, JSON.stringify(index, null, 2), 'utf8');
|
|
222
|
-
|
|
223
|
-
return index;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
/**
|
|
227
|
-
* Search index by pattern
|
|
228
|
-
* @param {string} query - Search query
|
|
229
|
-
* @param {Object} index - Loaded index
|
|
230
|
-
* @returns {string[]} Matching lesson IDs
|
|
231
|
-
*/
|
|
232
|
-
export function searchByPattern(query, index) {
|
|
233
|
-
const tokens = tokenizePattern(query);
|
|
234
|
-
const matchingIds = new Set();
|
|
235
|
-
|
|
236
|
-
for (const token of tokens) {
|
|
237
|
-
if (index.patternIndex[token]) {
|
|
238
|
-
for (const id of index.patternIndex[token]) {
|
|
239
|
-
matchingIds.add(id);
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
return [...matchingIds];
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
* Search index by tag
|
|
249
|
-
* @param {string} tag - Tag to search
|
|
250
|
-
* @param {Object} index - Loaded index
|
|
251
|
-
* @returns {string[]} Matching lesson IDs
|
|
252
|
-
*/
|
|
253
|
-
export function searchByTag(tag, index) {
|
|
254
|
-
return index.tagIndex[tag] || [];
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
/**
|
|
258
|
-
* Get lesson by ID
|
|
259
|
-
* @param {string} id - Lesson ID
|
|
260
|
-
* @param {Object} index - Loaded index
|
|
261
|
-
* @returns {Object|null} Lesson info
|
|
262
|
-
*/
|
|
263
|
-
export function getById(id, index) {
|
|
264
|
-
return index.idIndex[id] || null;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
/**
|
|
268
|
-
* CLI entry point
|
|
269
|
-
*/
|
|
270
|
-
function main() {
|
|
271
|
-
const args = process.argv.slice(2);
|
|
272
|
-
|
|
273
|
-
if (args.includes('--status')) {
|
|
274
|
-
const freshness = checkIndexFreshness();
|
|
275
|
-
if (freshness.stale) {
|
|
276
|
-
console.log(`⚠️ Index is STALE: ${freshness.reason}`);
|
|
277
|
-
process.exit(1);
|
|
278
|
-
} else {
|
|
279
|
-
const index = loadIndex();
|
|
280
|
-
console.log(`✅ Index is FRESH`);
|
|
281
|
-
console.log(` Generated: ${index.generatedAt}`);
|
|
282
|
-
console.log(` Lessons: ${index.stats.totalLessons}`);
|
|
283
|
-
console.log(` Patterns: ${index.stats.totalPatterns}`);
|
|
284
|
-
console.log(` Tags: ${index.stats.totalTags}`);
|
|
285
|
-
}
|
|
286
|
-
return;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
if (args.includes('--rebuild')) {
|
|
290
|
-
console.log('🔄 Rebuilding knowledge index...');
|
|
291
|
-
const index = rebuildIndex();
|
|
292
|
-
console.log(`✅ Index rebuilt successfully`);
|
|
293
|
-
console.log(` Lessons: ${index.stats.totalLessons}`);
|
|
294
|
-
console.log(` Patterns: ${index.stats.totalPatterns}`);
|
|
295
|
-
console.log(` Tags: ${index.stats.totalTags}`);
|
|
296
|
-
return;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
console.log(`
|
|
300
|
-
📇 Knowledge Index Manager
|
|
301
|
-
|
|
302
|
-
Usage:
|
|
303
|
-
agent index --rebuild Rebuild index from knowledge files
|
|
304
|
-
agent index --status Check if index is fresh or stale
|
|
305
|
-
|
|
306
|
-
The index provides O(1) lookup for:
|
|
307
|
-
- Pattern matching (by keyword)
|
|
308
|
-
- Tag filtering
|
|
309
|
-
- Severity grouping
|
|
310
|
-
- Direct ID lookup
|
|
311
|
-
`);
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
// Run if called directly
|
|
315
|
-
if (process.argv[1]?.includes('knowledge-index')) {
|
|
316
|
-
main();
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
export default {
|
|
320
|
-
loadIndex,
|
|
321
|
-
rebuildIndex,
|
|
322
|
-
checkIndexFreshness,
|
|
323
|
-
searchByPattern,
|
|
324
|
-
searchByTag,
|
|
325
|
-
getById
|
|
326
|
-
};
|