@vpxa/kb 0.1.1
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/LICENSE +21 -0
- package/README.md +1140 -0
- package/bin/kb.mjs +10 -0
- package/package.json +67 -0
- package/packages/analyzers/dist/blast-radius-analyzer.d.ts +23 -0
- package/packages/analyzers/dist/blast-radius-analyzer.js +114 -0
- package/packages/analyzers/dist/dependency-analyzer.d.ts +29 -0
- package/packages/analyzers/dist/dependency-analyzer.js +425 -0
- package/packages/analyzers/dist/diagram-generator.d.ts +13 -0
- package/packages/analyzers/dist/diagram-generator.js +86 -0
- package/packages/analyzers/dist/entry-point-analyzer.d.ts +19 -0
- package/packages/analyzers/dist/entry-point-analyzer.js +239 -0
- package/packages/analyzers/dist/index.d.ts +14 -0
- package/packages/analyzers/dist/index.js +23 -0
- package/packages/analyzers/dist/knowledge-producer.d.ts +32 -0
- package/packages/analyzers/dist/knowledge-producer.js +113 -0
- package/packages/analyzers/dist/pattern-analyzer.d.ts +12 -0
- package/packages/analyzers/dist/pattern-analyzer.js +359 -0
- package/packages/analyzers/dist/regex-call-graph.d.ts +17 -0
- package/packages/analyzers/dist/regex-call-graph.js +428 -0
- package/packages/analyzers/dist/structure-analyzer.d.ts +11 -0
- package/packages/analyzers/dist/structure-analyzer.js +258 -0
- package/packages/analyzers/dist/symbol-analyzer.d.ts +10 -0
- package/packages/analyzers/dist/symbol-analyzer.js +442 -0
- package/packages/analyzers/dist/ts-call-graph.d.ts +27 -0
- package/packages/analyzers/dist/ts-call-graph.js +160 -0
- package/packages/analyzers/dist/types.d.ts +98 -0
- package/packages/analyzers/dist/types.js +1 -0
- package/packages/chunker/dist/call-graph-extractor.d.ts +22 -0
- package/packages/chunker/dist/call-graph-extractor.js +90 -0
- package/packages/chunker/dist/chunker-factory.d.ts +7 -0
- package/packages/chunker/dist/chunker-factory.js +36 -0
- package/packages/chunker/dist/chunker.interface.d.ts +10 -0
- package/packages/chunker/dist/chunker.interface.js +1 -0
- package/packages/chunker/dist/code-chunker.d.ts +14 -0
- package/packages/chunker/dist/code-chunker.js +134 -0
- package/packages/chunker/dist/generic-chunker.d.ts +12 -0
- package/packages/chunker/dist/generic-chunker.js +72 -0
- package/packages/chunker/dist/index.d.ts +8 -0
- package/packages/chunker/dist/index.js +21 -0
- package/packages/chunker/dist/markdown-chunker.d.ts +14 -0
- package/packages/chunker/dist/markdown-chunker.js +122 -0
- package/packages/chunker/dist/treesitter-chunker.d.ts +47 -0
- package/packages/chunker/dist/treesitter-chunker.js +234 -0
- package/packages/cli/dist/commands/analyze.d.ts +3 -0
- package/packages/cli/dist/commands/analyze.js +112 -0
- package/packages/cli/dist/commands/context-cmds.d.ts +3 -0
- package/packages/cli/dist/commands/context-cmds.js +155 -0
- package/packages/cli/dist/commands/environment.d.ts +3 -0
- package/packages/cli/dist/commands/environment.js +204 -0
- package/packages/cli/dist/commands/execution.d.ts +3 -0
- package/packages/cli/dist/commands/execution.js +137 -0
- package/packages/cli/dist/commands/graph.d.ts +3 -0
- package/packages/cli/dist/commands/graph.js +81 -0
- package/packages/cli/dist/commands/init.d.ts +8 -0
- package/packages/cli/dist/commands/init.js +87 -0
- package/packages/cli/dist/commands/knowledge.d.ts +3 -0
- package/packages/cli/dist/commands/knowledge.js +139 -0
- package/packages/cli/dist/commands/search.d.ts +3 -0
- package/packages/cli/dist/commands/search.js +267 -0
- package/packages/cli/dist/commands/system.d.ts +3 -0
- package/packages/cli/dist/commands/system.js +241 -0
- package/packages/cli/dist/commands/workspace.d.ts +3 -0
- package/packages/cli/dist/commands/workspace.js +388 -0
- package/packages/cli/dist/context.d.ts +5 -0
- package/packages/cli/dist/context.js +14 -0
- package/packages/cli/dist/helpers.d.ts +52 -0
- package/packages/cli/dist/helpers.js +458 -0
- package/packages/cli/dist/index.d.ts +8 -0
- package/packages/cli/dist/index.js +69 -0
- package/packages/cli/dist/kb-init.d.ts +57 -0
- package/packages/cli/dist/kb-init.js +82 -0
- package/packages/cli/dist/types.d.ts +7 -0
- package/packages/cli/dist/types.js +1 -0
- package/packages/core/dist/constants.d.ts +49 -0
- package/packages/core/dist/constants.js +43 -0
- package/packages/core/dist/content-detector.d.ts +9 -0
- package/packages/core/dist/content-detector.js +79 -0
- package/packages/core/dist/errors.d.ts +18 -0
- package/packages/core/dist/errors.js +40 -0
- package/packages/core/dist/index.d.ts +6 -0
- package/packages/core/dist/index.js +9 -0
- package/packages/core/dist/logger.d.ts +9 -0
- package/packages/core/dist/logger.js +34 -0
- package/packages/core/dist/types.d.ts +108 -0
- package/packages/core/dist/types.js +1 -0
- package/packages/embeddings/dist/embedder.interface.d.ts +24 -0
- package/packages/embeddings/dist/embedder.interface.js +1 -0
- package/packages/embeddings/dist/index.d.ts +3 -0
- package/packages/embeddings/dist/index.js +5 -0
- package/packages/embeddings/dist/onnx-embedder.d.ts +24 -0
- package/packages/embeddings/dist/onnx-embedder.js +82 -0
- package/packages/indexer/dist/file-hasher.d.ts +11 -0
- package/packages/indexer/dist/file-hasher.js +13 -0
- package/packages/indexer/dist/filesystem-crawler.d.ts +27 -0
- package/packages/indexer/dist/filesystem-crawler.js +125 -0
- package/packages/indexer/dist/graph-extractor.d.ts +22 -0
- package/packages/indexer/dist/graph-extractor.js +111 -0
- package/packages/indexer/dist/incremental-indexer.d.ts +47 -0
- package/packages/indexer/dist/incremental-indexer.js +278 -0
- package/packages/indexer/dist/index.d.ts +5 -0
- package/packages/indexer/dist/index.js +14 -0
- package/packages/server/dist/api.d.ts +8 -0
- package/packages/server/dist/api.js +9 -0
- package/packages/server/dist/config.d.ts +3 -0
- package/packages/server/dist/config.js +75 -0
- package/packages/server/dist/curated-manager.d.ts +86 -0
- package/packages/server/dist/curated-manager.js +357 -0
- package/packages/server/dist/index.d.ts +2 -0
- package/packages/server/dist/index.js +134 -0
- package/packages/server/dist/replay-interceptor.d.ts +11 -0
- package/packages/server/dist/replay-interceptor.js +38 -0
- package/packages/server/dist/resources/resources.d.ts +4 -0
- package/packages/server/dist/resources/resources.js +40 -0
- package/packages/server/dist/server.d.ts +21 -0
- package/packages/server/dist/server.js +247 -0
- package/packages/server/dist/tools/analyze.tools.d.ts +11 -0
- package/packages/server/dist/tools/analyze.tools.js +288 -0
- package/packages/server/dist/tools/forge.tools.d.ts +12 -0
- package/packages/server/dist/tools/forge.tools.js +501 -0
- package/packages/server/dist/tools/forget.tool.d.ts +4 -0
- package/packages/server/dist/tools/forget.tool.js +43 -0
- package/packages/server/dist/tools/graph.tool.d.ts +4 -0
- package/packages/server/dist/tools/graph.tool.js +110 -0
- package/packages/server/dist/tools/list.tool.d.ts +4 -0
- package/packages/server/dist/tools/list.tool.js +56 -0
- package/packages/server/dist/tools/lookup.tool.d.ts +4 -0
- package/packages/server/dist/tools/lookup.tool.js +53 -0
- package/packages/server/dist/tools/onboard.tool.d.ts +5 -0
- package/packages/server/dist/tools/onboard.tool.js +112 -0
- package/packages/server/dist/tools/produce.tool.d.ts +3 -0
- package/packages/server/dist/tools/produce.tool.js +74 -0
- package/packages/server/dist/tools/read.tool.d.ts +4 -0
- package/packages/server/dist/tools/read.tool.js +49 -0
- package/packages/server/dist/tools/reindex.tool.d.ts +7 -0
- package/packages/server/dist/tools/reindex.tool.js +70 -0
- package/packages/server/dist/tools/remember.tool.d.ts +4 -0
- package/packages/server/dist/tools/remember.tool.js +45 -0
- package/packages/server/dist/tools/replay.tool.d.ts +3 -0
- package/packages/server/dist/tools/replay.tool.js +89 -0
- package/packages/server/dist/tools/search.tool.d.ts +5 -0
- package/packages/server/dist/tools/search.tool.js +331 -0
- package/packages/server/dist/tools/status.tool.d.ts +4 -0
- package/packages/server/dist/tools/status.tool.js +68 -0
- package/packages/server/dist/tools/toolkit.tools.d.ts +35 -0
- package/packages/server/dist/tools/toolkit.tools.js +1674 -0
- package/packages/server/dist/tools/update.tool.d.ts +4 -0
- package/packages/server/dist/tools/update.tool.js +42 -0
- package/packages/server/dist/tools/utility.tools.d.ts +15 -0
- package/packages/server/dist/tools/utility.tools.js +461 -0
- package/packages/store/dist/graph-store.interface.d.ts +104 -0
- package/packages/store/dist/graph-store.interface.js +1 -0
- package/packages/store/dist/index.d.ts +6 -0
- package/packages/store/dist/index.js +9 -0
- package/packages/store/dist/lance-store.d.ts +32 -0
- package/packages/store/dist/lance-store.js +258 -0
- package/packages/store/dist/sqlite-graph-store.d.ts +43 -0
- package/packages/store/dist/sqlite-graph-store.js +374 -0
- package/packages/store/dist/store-factory.d.ts +9 -0
- package/packages/store/dist/store-factory.js +14 -0
- package/packages/store/dist/store.interface.d.ts +48 -0
- package/packages/store/dist/store.interface.js +1 -0
- package/packages/tools/dist/batch.d.ts +21 -0
- package/packages/tools/dist/batch.js +45 -0
- package/packages/tools/dist/changelog.d.ts +34 -0
- package/packages/tools/dist/changelog.js +112 -0
- package/packages/tools/dist/check.d.ts +26 -0
- package/packages/tools/dist/check.js +59 -0
- package/packages/tools/dist/checkpoint.d.ts +17 -0
- package/packages/tools/dist/checkpoint.js +43 -0
- package/packages/tools/dist/codemod.d.ts +37 -0
- package/packages/tools/dist/codemod.js +69 -0
- package/packages/tools/dist/compact.d.ts +41 -0
- package/packages/tools/dist/compact.js +60 -0
- package/packages/tools/dist/data-transform.d.ts +10 -0
- package/packages/tools/dist/data-transform.js +124 -0
- package/packages/tools/dist/dead-symbols.d.ts +21 -0
- package/packages/tools/dist/dead-symbols.js +71 -0
- package/packages/tools/dist/delegate.d.ts +34 -0
- package/packages/tools/dist/delegate.js +130 -0
- package/packages/tools/dist/diff-parse.d.ts +26 -0
- package/packages/tools/dist/diff-parse.js +153 -0
- package/packages/tools/dist/digest.d.ts +53 -0
- package/packages/tools/dist/digest.js +242 -0
- package/packages/tools/dist/encode.d.ts +14 -0
- package/packages/tools/dist/encode.js +46 -0
- package/packages/tools/dist/env-info.d.ts +28 -0
- package/packages/tools/dist/env-info.js +58 -0
- package/packages/tools/dist/eval.d.ts +13 -0
- package/packages/tools/dist/eval.js +79 -0
- package/packages/tools/dist/evidence-map.d.ts +79 -0
- package/packages/tools/dist/evidence-map.js +203 -0
- package/packages/tools/dist/file-summary.d.ts +32 -0
- package/packages/tools/dist/file-summary.js +106 -0
- package/packages/tools/dist/file-walk.d.ts +4 -0
- package/packages/tools/dist/file-walk.js +75 -0
- package/packages/tools/dist/find-examples.d.ts +25 -0
- package/packages/tools/dist/find-examples.js +48 -0
- package/packages/tools/dist/find.d.ts +47 -0
- package/packages/tools/dist/find.js +120 -0
- package/packages/tools/dist/forge-classify.d.ts +44 -0
- package/packages/tools/dist/forge-classify.js +319 -0
- package/packages/tools/dist/forge-ground.d.ts +64 -0
- package/packages/tools/dist/forge-ground.js +184 -0
- package/packages/tools/dist/git-context.d.ts +22 -0
- package/packages/tools/dist/git-context.js +46 -0
- package/packages/tools/dist/graph-query.d.ts +89 -0
- package/packages/tools/dist/graph-query.js +194 -0
- package/packages/tools/dist/health.d.ts +14 -0
- package/packages/tools/dist/health.js +118 -0
- package/packages/tools/dist/http-request.d.ts +23 -0
- package/packages/tools/dist/http-request.js +58 -0
- package/packages/tools/dist/index.d.ts +49 -0
- package/packages/tools/dist/index.js +273 -0
- package/packages/tools/dist/lane.d.ts +39 -0
- package/packages/tools/dist/lane.js +227 -0
- package/packages/tools/dist/measure.d.ts +38 -0
- package/packages/tools/dist/measure.js +119 -0
- package/packages/tools/dist/onboard.d.ts +41 -0
- package/packages/tools/dist/onboard.js +1139 -0
- package/packages/tools/dist/parse-output.d.ts +80 -0
- package/packages/tools/dist/parse-output.js +158 -0
- package/packages/tools/dist/process-manager.d.ts +18 -0
- package/packages/tools/dist/process-manager.js +69 -0
- package/packages/tools/dist/queue.d.ts +38 -0
- package/packages/tools/dist/queue.js +126 -0
- package/packages/tools/dist/regex-test.d.ts +31 -0
- package/packages/tools/dist/regex-test.js +39 -0
- package/packages/tools/dist/rename.d.ts +29 -0
- package/packages/tools/dist/rename.js +70 -0
- package/packages/tools/dist/replay.d.ts +56 -0
- package/packages/tools/dist/replay.js +108 -0
- package/packages/tools/dist/schema-validate.d.ts +23 -0
- package/packages/tools/dist/schema-validate.js +141 -0
- package/packages/tools/dist/scope-map.d.ts +52 -0
- package/packages/tools/dist/scope-map.js +72 -0
- package/packages/tools/dist/snippet.d.ts +34 -0
- package/packages/tools/dist/snippet.js +80 -0
- package/packages/tools/dist/stash.d.ts +12 -0
- package/packages/tools/dist/stash.js +60 -0
- package/packages/tools/dist/stratum-card.d.ts +31 -0
- package/packages/tools/dist/stratum-card.js +239 -0
- package/packages/tools/dist/symbol.d.ts +28 -0
- package/packages/tools/dist/symbol.js +87 -0
- package/packages/tools/dist/test-run.d.ts +23 -0
- package/packages/tools/dist/test-run.js +55 -0
- package/packages/tools/dist/text-utils.d.ts +16 -0
- package/packages/tools/dist/text-utils.js +31 -0
- package/packages/tools/dist/time-utils.d.ts +18 -0
- package/packages/tools/dist/time-utils.js +135 -0
- package/packages/tools/dist/trace.d.ts +24 -0
- package/packages/tools/dist/trace.js +114 -0
- package/packages/tools/dist/truncation.d.ts +22 -0
- package/packages/tools/dist/truncation.js +45 -0
- package/packages/tools/dist/watch.d.ts +30 -0
- package/packages/tools/dist/watch.js +61 -0
- package/packages/tools/dist/web-fetch.d.ts +45 -0
- package/packages/tools/dist/web-fetch.js +249 -0
- package/packages/tools/dist/web-search.d.ts +23 -0
- package/packages/tools/dist/web-search.js +46 -0
- package/packages/tools/dist/workset.d.ts +45 -0
- package/packages/tools/dist/workset.js +77 -0
- package/packages/tui/dist/App.d.ts +8 -0
- package/packages/tui/dist/App.js +52659 -0
- package/packages/tui/dist/index.d.ts +19 -0
- package/packages/tui/dist/index.js +54742 -0
- package/packages/tui/dist/panels/CuratedPanel.d.ts +8 -0
- package/packages/tui/dist/panels/CuratedPanel.js +34452 -0
- package/packages/tui/dist/panels/LogPanel.d.ts +3 -0
- package/packages/tui/dist/panels/LogPanel.js +51894 -0
- package/packages/tui/dist/panels/SearchPanel.d.ts +10 -0
- package/packages/tui/dist/panels/SearchPanel.js +34985 -0
- package/packages/tui/dist/panels/StatusPanel.d.ts +8 -0
- package/packages/tui/dist/panels/StatusPanel.js +34465 -0
- package/skills/knowledge-base/SKILL.md +316 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* kb_forge_classify — Automated FORGE tier classification.
|
|
3
|
+
*
|
|
4
|
+
* Determines Floor/Standard/Critical tier from file analysis.
|
|
5
|
+
* Uses path patterns, export scanning, and structural heuristics.
|
|
6
|
+
* No LLM needed.
|
|
7
|
+
*/
|
|
8
|
+
import type { ForgeTier } from './evidence-map.js';
|
|
9
|
+
export interface ForgeClassifyOptions {
|
|
10
|
+
/** Files being modified (absolute or relative paths) */
|
|
11
|
+
files: string[];
|
|
12
|
+
/** Task description for keyword detection */
|
|
13
|
+
task: string;
|
|
14
|
+
/** Root path for package boundary detection */
|
|
15
|
+
rootPath: string;
|
|
16
|
+
}
|
|
17
|
+
export interface ClassifyTrigger {
|
|
18
|
+
rule: string;
|
|
19
|
+
detail: string;
|
|
20
|
+
source: 'blast_radius' | 'cross_package' | 'schema_contract' | 'security_auth' | 'task_hint' | 'default';
|
|
21
|
+
}
|
|
22
|
+
export interface TypedUnknownSeed {
|
|
23
|
+
description: string;
|
|
24
|
+
type: 'contract' | 'convention' | 'freshness' | 'runtime' | 'data-flow' | 'impact';
|
|
25
|
+
suggestedTool: string;
|
|
26
|
+
}
|
|
27
|
+
export interface ForgeClassifyCeremony {
|
|
28
|
+
ground: string;
|
|
29
|
+
build: string;
|
|
30
|
+
break: string;
|
|
31
|
+
evidenceMap: string;
|
|
32
|
+
gate: string;
|
|
33
|
+
}
|
|
34
|
+
export interface ForgeClassifyResult {
|
|
35
|
+
tier: ForgeTier;
|
|
36
|
+
triggers: ClassifyTrigger[];
|
|
37
|
+
packagesCrossed: string[];
|
|
38
|
+
hasSchemaChange: boolean;
|
|
39
|
+
hasSecurityPath: boolean;
|
|
40
|
+
typedUnknownSeeds: TypedUnknownSeed[];
|
|
41
|
+
ceremony: ForgeClassifyCeremony;
|
|
42
|
+
}
|
|
43
|
+
export declare function forgeClassify(options: ForgeClassifyOptions): Promise<ForgeClassifyResult>;
|
|
44
|
+
//# sourceMappingURL=forge-classify.d.ts.map
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
2
|
+
import { dirname, extname, relative, resolve } from "node:path";
|
|
3
|
+
import { fileSummary } from "./file-summary.js";
|
|
4
|
+
const MAX_SCAN_BYTES = 1e5;
|
|
5
|
+
const MAX_SCAN_LINES = 200;
|
|
6
|
+
const SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mts", ".cts", ".mjs", ".cjs"]);
|
|
7
|
+
const EXCLUDED_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
8
|
+
".git",
|
|
9
|
+
".kb-data",
|
|
10
|
+
".kb-state",
|
|
11
|
+
".turbo",
|
|
12
|
+
".yarn",
|
|
13
|
+
"build",
|
|
14
|
+
"coverage",
|
|
15
|
+
"dist",
|
|
16
|
+
"node_modules"
|
|
17
|
+
]);
|
|
18
|
+
const SECURITY_PATH_PATTERN = /auth|token|permission|acl|encrypt|secret|credential|jwt|oauth|password/i;
|
|
19
|
+
const SECURITY_CONTENT_PATTERN = /\b(hash|sign|verify|bcrypt|jwt|decrypt|secret|password)\b/i;
|
|
20
|
+
const SECURITY_TASK_PATTERN = /auth|security|permission|encrypt|secret|credential/i;
|
|
21
|
+
const SCHEMA_PATH_PATTERN = /types\.ts$|schema\.ts$|contract\.ts$|\.proto$|openapi|swagger|\.graphql$/i;
|
|
22
|
+
const SHARED_PATH_PATTERN = /(?:^|\/)(events|contracts|shared)(?:\/|$)/i;
|
|
23
|
+
const SCHEMA_CONTENT_PATTERN = /export\s+interface\b|export\s+type\b|export\s+const\s+\w*Schema\w*\s*=\s*z\./i;
|
|
24
|
+
const SCHEMA_TASK_PATTERN = /schema|contract|migration|breaking.change|api.change/i;
|
|
25
|
+
const TASK_CRITICAL_PATTERN = /migration|data.?model|multi.?service|breaking|backward.?compat/i;
|
|
26
|
+
const CEREMONIES = {
|
|
27
|
+
floor: {
|
|
28
|
+
ground: "Parasitic \u2014 read target file only",
|
|
29
|
+
build: "Implement directly",
|
|
30
|
+
break: "Skip",
|
|
31
|
+
evidenceMap: "Not required",
|
|
32
|
+
gate: "Self-certify"
|
|
33
|
+
},
|
|
34
|
+
standard: {
|
|
35
|
+
ground: "Scope map + blast radius + constraint seed",
|
|
36
|
+
build: "TDD \u2014 test first, then implement",
|
|
37
|
+
break: "Error paths + edge cases",
|
|
38
|
+
evidenceMap: "3-8 critical-path entries",
|
|
39
|
+
gate: "YIELD/HOLD evaluation"
|
|
40
|
+
},
|
|
41
|
+
critical: {
|
|
42
|
+
ground: "Full scope map + blast radius + trace + patterns + constraint pack",
|
|
43
|
+
build: "TDD + contract verification + cross-service validation",
|
|
44
|
+
break: "Error paths + edge cases + security dimensions + data-flow verification",
|
|
45
|
+
evidenceMap: "Comprehensive \u2014 all critical-path claims with receipts",
|
|
46
|
+
gate: "Strict YIELD/HOLD/HARD_BLOCK evaluation"
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
async function forgeClassify(options) {
|
|
50
|
+
const rootPath = resolve(options.rootPath);
|
|
51
|
+
const task = options.task.trim();
|
|
52
|
+
const files = options.files.map((filePath) => resolve(rootPath, filePath));
|
|
53
|
+
const triggers = [];
|
|
54
|
+
let hasSecurityPath = false;
|
|
55
|
+
let hasSchemaChange = false;
|
|
56
|
+
for (const filePath of files) {
|
|
57
|
+
const normalizedPath = normalizeForMatch(filePath, rootPath);
|
|
58
|
+
const scanText = readScanText(filePath);
|
|
59
|
+
if (SECURITY_PATH_PATTERN.test(normalizedPath) || SECURITY_CONTENT_PATTERN.test(scanText) || SECURITY_TASK_PATTERN.test(task)) {
|
|
60
|
+
hasSecurityPath = true;
|
|
61
|
+
}
|
|
62
|
+
if (SCHEMA_PATH_PATTERN.test(normalizedPath) || SHARED_PATH_PATTERN.test(normalizedPath) || SCHEMA_TASK_PATTERN.test(task) || SCHEMA_CONTENT_PATTERN.test(scanText) || await hasExportedContractShape(filePath)) {
|
|
63
|
+
hasSchemaChange = true;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (hasSecurityPath) {
|
|
67
|
+
addTrigger(triggers, {
|
|
68
|
+
rule: "security-path",
|
|
69
|
+
detail: "Security/auth path, task, or content matched security heuristics",
|
|
70
|
+
source: "security_auth"
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
if (hasSchemaChange) {
|
|
74
|
+
addTrigger(triggers, {
|
|
75
|
+
rule: "schema-contract",
|
|
76
|
+
detail: "Schema or contract path, task, or export shape matched contract heuristics",
|
|
77
|
+
source: "schema_contract"
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
const blastRadius = countDirectImportBlastRadius(files, rootPath);
|
|
81
|
+
if (blastRadius.affectedFiles > 5) {
|
|
82
|
+
addTrigger(triggers, {
|
|
83
|
+
rule: "blast-radius-importers",
|
|
84
|
+
detail: `${blastRadius.affectedFiles} affected files via direct import scanning`,
|
|
85
|
+
source: "blast_radius"
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
const packagesCrossed = [
|
|
89
|
+
...new Set(
|
|
90
|
+
files.map((filePath) => findPackageName(filePath, rootPath)).filter((packageName) => Boolean(packageName))
|
|
91
|
+
)
|
|
92
|
+
].sort();
|
|
93
|
+
if (packagesCrossed.length >= 2) {
|
|
94
|
+
addTrigger(triggers, {
|
|
95
|
+
rule: "cross-package-boundary",
|
|
96
|
+
detail: `Files span ${packagesCrossed.length} packages: ${packagesCrossed.join(", ")}`,
|
|
97
|
+
source: "cross_package"
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
if (TASK_CRITICAL_PATTERN.test(task)) {
|
|
101
|
+
addTrigger(triggers, {
|
|
102
|
+
rule: "task-hint-critical",
|
|
103
|
+
detail: "Task description matched migration or compatibility criticality hints",
|
|
104
|
+
source: "task_hint"
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
const tier = triggers.length > 0 ? "critical" : files.length === 1 ? "floor" : "standard";
|
|
108
|
+
return {
|
|
109
|
+
tier,
|
|
110
|
+
triggers,
|
|
111
|
+
packagesCrossed,
|
|
112
|
+
hasSchemaChange,
|
|
113
|
+
hasSecurityPath,
|
|
114
|
+
typedUnknownSeeds: buildTypedUnknownSeeds(triggers),
|
|
115
|
+
ceremony: CEREMONIES[tier]
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
function addTrigger(triggers, trigger) {
|
|
119
|
+
if (triggers.some(
|
|
120
|
+
(existing) => existing.rule === trigger.rule && existing.source === trigger.source
|
|
121
|
+
)) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
triggers.push(trigger);
|
|
125
|
+
}
|
|
126
|
+
function normalizeForMatch(filePath, rootPath) {
|
|
127
|
+
const absolutePath = resolve(rootPath, filePath);
|
|
128
|
+
const displayPath = relative(rootPath, absolutePath);
|
|
129
|
+
const safePath = displayPath && !displayPath.startsWith("..") ? displayPath : absolutePath;
|
|
130
|
+
return safePath.replace(/\\/g, "/");
|
|
131
|
+
}
|
|
132
|
+
function canScanFile(filePath) {
|
|
133
|
+
if (!existsSync(filePath)) {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
try {
|
|
137
|
+
return statSync(filePath).size <= MAX_SCAN_BYTES;
|
|
138
|
+
} catch {
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function readScanText(filePath) {
|
|
143
|
+
if (!canScanFile(filePath)) {
|
|
144
|
+
return "";
|
|
145
|
+
}
|
|
146
|
+
try {
|
|
147
|
+
return readFileSync(filePath, "utf-8").split(/\r?\n/).slice(0, MAX_SCAN_LINES).join("\n");
|
|
148
|
+
} catch {
|
|
149
|
+
return "";
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
async function hasExportedContractShape(filePath) {
|
|
153
|
+
if (!canScanFile(filePath)) {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
try {
|
|
157
|
+
const summary = await fileSummary({ path: filePath });
|
|
158
|
+
const exportedNames = new Set(summary.exports);
|
|
159
|
+
return summary.interfaces.some((entry) => exportedNames.has(entry.name)) || summary.types.some((entry) => exportedNames.has(entry.name));
|
|
160
|
+
} catch {
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
function countDirectImportBlastRadius(targetFiles, rootPath) {
|
|
165
|
+
const existingTargets = new Set(targetFiles.filter((filePath) => existsSync(filePath)));
|
|
166
|
+
if (existingTargets.size === 0) {
|
|
167
|
+
return { affectedFiles: targetFiles.length, importers: [] };
|
|
168
|
+
}
|
|
169
|
+
const importers = /* @__PURE__ */ new Set();
|
|
170
|
+
for (const candidateFile of collectSourceFiles(rootPath)) {
|
|
171
|
+
if (existingTargets.has(candidateFile) || !canScanFile(candidateFile)) {
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
const scanText = readScanText(candidateFile);
|
|
175
|
+
if (!scanText) {
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
const specifiers = extractImportSpecifiers(scanText);
|
|
179
|
+
if (specifiers.some((specifier) => resolvesToTarget(specifier, candidateFile, existingTargets))) {
|
|
180
|
+
importers.add(candidateFile);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
affectedFiles: targetFiles.length + importers.size,
|
|
185
|
+
importers: [...importers].map((filePath) => relative(rootPath, filePath).replace(/\\/g, "/"))
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
function collectSourceFiles(rootPath) {
|
|
189
|
+
const files = [];
|
|
190
|
+
function walk(currentPath) {
|
|
191
|
+
let entries = [];
|
|
192
|
+
try {
|
|
193
|
+
entries = readdirSync(currentPath);
|
|
194
|
+
} catch {
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
for (const entry of entries) {
|
|
198
|
+
if (EXCLUDED_DIRECTORIES.has(entry)) {
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
const fullPath = resolve(currentPath, entry);
|
|
202
|
+
let entryStat;
|
|
203
|
+
try {
|
|
204
|
+
entryStat = statSync(fullPath);
|
|
205
|
+
} catch {
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
if (entryStat.isDirectory()) {
|
|
209
|
+
walk(fullPath);
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
if (SOURCE_EXTENSIONS.has(extname(entry).toLowerCase())) {
|
|
213
|
+
files.push(fullPath);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
walk(rootPath);
|
|
218
|
+
return files;
|
|
219
|
+
}
|
|
220
|
+
function extractImportSpecifiers(content) {
|
|
221
|
+
const specifiers = /* @__PURE__ */ new Set();
|
|
222
|
+
const pattern = /(?:from\s+['"]([^'"]+)['"]|import\s+['"]([^'"]+)['"]|require\(\s*['"]([^'"]+)['"]\s*\))/g;
|
|
223
|
+
for (const match of content.matchAll(pattern)) {
|
|
224
|
+
const specifier = match[1] ?? match[2] ?? match[3];
|
|
225
|
+
if (specifier) {
|
|
226
|
+
specifiers.add(specifier);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return [...specifiers];
|
|
230
|
+
}
|
|
231
|
+
function resolvesToTarget(specifier, importerFile, targets) {
|
|
232
|
+
if (!specifier.startsWith(".")) {
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
const basePath = resolve(dirname(importerFile), specifier);
|
|
236
|
+
const candidates = [
|
|
237
|
+
basePath,
|
|
238
|
+
`${basePath}.ts`,
|
|
239
|
+
`${basePath}.tsx`,
|
|
240
|
+
`${basePath}.js`,
|
|
241
|
+
`${basePath}.jsx`,
|
|
242
|
+
`${basePath}.mts`,
|
|
243
|
+
`${basePath}.cts`,
|
|
244
|
+
`${basePath}.mjs`,
|
|
245
|
+
`${basePath}.cjs`,
|
|
246
|
+
resolve(basePath, "index.ts"),
|
|
247
|
+
resolve(basePath, "index.tsx"),
|
|
248
|
+
resolve(basePath, "index.js"),
|
|
249
|
+
resolve(basePath, "index.jsx")
|
|
250
|
+
];
|
|
251
|
+
return candidates.some((candidate) => targets.has(candidate));
|
|
252
|
+
}
|
|
253
|
+
function buildTypedUnknownSeeds(triggers) {
|
|
254
|
+
return triggers.map((trigger) => {
|
|
255
|
+
switch (trigger.source) {
|
|
256
|
+
case "security_auth":
|
|
257
|
+
return {
|
|
258
|
+
description: "Verify auth and security assumptions before yielding",
|
|
259
|
+
type: "contract",
|
|
260
|
+
suggestedTool: "kb_search"
|
|
261
|
+
};
|
|
262
|
+
case "schema_contract":
|
|
263
|
+
return {
|
|
264
|
+
description: "Confirm schema and contract compatibility",
|
|
265
|
+
type: "contract",
|
|
266
|
+
suggestedTool: "kb_schema_validate"
|
|
267
|
+
};
|
|
268
|
+
case "blast_radius":
|
|
269
|
+
return {
|
|
270
|
+
description: "Inspect affected importers before delivery",
|
|
271
|
+
type: "impact",
|
|
272
|
+
suggestedTool: "kb_blast_radius"
|
|
273
|
+
};
|
|
274
|
+
case "cross_package":
|
|
275
|
+
return {
|
|
276
|
+
description: "Assess downstream package impact across boundaries",
|
|
277
|
+
type: "impact",
|
|
278
|
+
suggestedTool: "kb_blast_radius"
|
|
279
|
+
};
|
|
280
|
+
case "task_hint":
|
|
281
|
+
return {
|
|
282
|
+
description: "Check established conventions for migrations or compatibility work",
|
|
283
|
+
type: "convention",
|
|
284
|
+
suggestedTool: "kb_find_examples"
|
|
285
|
+
};
|
|
286
|
+
default:
|
|
287
|
+
return {
|
|
288
|
+
description: "No explicit unknown routing required",
|
|
289
|
+
type: "freshness",
|
|
290
|
+
suggestedTool: "kb_lookup"
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
function findPackageName(filePath, rootPath) {
|
|
296
|
+
let dir = dirname(resolve(rootPath, filePath));
|
|
297
|
+
const root = resolve(rootPath);
|
|
298
|
+
while (dir.length >= root.length) {
|
|
299
|
+
const packageJsonPath = resolve(dir, "package.json");
|
|
300
|
+
if (existsSync(packageJsonPath)) {
|
|
301
|
+
try {
|
|
302
|
+
const pkg = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
303
|
+
return pkg.name ?? relative(root, dir).replace(/\\/g, "/");
|
|
304
|
+
} catch {
|
|
305
|
+
return relative(root, dir).replace(/\\/g, "/");
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
const parent = dirname(dir);
|
|
309
|
+
if (parent === dir) {
|
|
310
|
+
break;
|
|
311
|
+
}
|
|
312
|
+
dir = parent;
|
|
313
|
+
}
|
|
314
|
+
return void 0;
|
|
315
|
+
}
|
|
316
|
+
export {
|
|
317
|
+
forgeClassify
|
|
318
|
+
};
|
|
319
|
+
//# sourceMappingURL=forge-classify.js.map
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* kb_forge_ground — FORGE Phase 1 (Ground) batch executor.
|
|
3
|
+
*
|
|
4
|
+
* Executes the complete Ground phase as a single tool call:
|
|
5
|
+
* tier classification → scope map → typed unknowns → constraint seed → file summaries.
|
|
6
|
+
*/
|
|
7
|
+
import type { IEmbedder } from '@kb/embeddings';
|
|
8
|
+
import type { IKnowledgeStore } from '@kb/store';
|
|
9
|
+
import { type ForgeTier } from './evidence-map.js';
|
|
10
|
+
import { type ClassifyTrigger, type TypedUnknownSeed } from './forge-classify.js';
|
|
11
|
+
import { type ScopeMapResult } from './scope-map.js';
|
|
12
|
+
export interface ForgeGroundOptions {
|
|
13
|
+
/** Task description */
|
|
14
|
+
task: string;
|
|
15
|
+
/** Target files being modified (absolute paths) */
|
|
16
|
+
files: string[];
|
|
17
|
+
/** Root path of the codebase */
|
|
18
|
+
rootPath: string;
|
|
19
|
+
/** Max constraint entries to load (default: 3) */
|
|
20
|
+
maxConstraints?: number;
|
|
21
|
+
/** Force a specific tier (skip auto-classify) */
|
|
22
|
+
forceTier?: ForgeTier;
|
|
23
|
+
/** Task ID for evidence map creation (default: auto-generated from task) */
|
|
24
|
+
taskId?: string;
|
|
25
|
+
}
|
|
26
|
+
export interface ConstraintRef {
|
|
27
|
+
source: string;
|
|
28
|
+
snippet: string;
|
|
29
|
+
relevance: number;
|
|
30
|
+
}
|
|
31
|
+
export interface ForgeGroundResult {
|
|
32
|
+
/** Computed or forced tier */
|
|
33
|
+
tier: ForgeTier;
|
|
34
|
+
/** Classification triggers (empty if tier was forced) */
|
|
35
|
+
classifyTriggers: ClassifyTrigger[];
|
|
36
|
+
/** Scope map (relevant files with focus ranges) — null for Floor tier */
|
|
37
|
+
scopeMap: ScopeMapResult | null;
|
|
38
|
+
/** Typed Unknown Queue seeds */
|
|
39
|
+
typedUnknownSeeds: TypedUnknownSeed[];
|
|
40
|
+
/** Constraint seed — relevant KB entries from decisions/patterns */
|
|
41
|
+
constraints: ConstraintRef[];
|
|
42
|
+
/** File summaries for target files */
|
|
43
|
+
fileSummaries: Array<{
|
|
44
|
+
path: string;
|
|
45
|
+
exports: string[];
|
|
46
|
+
functions: string[];
|
|
47
|
+
lines: number;
|
|
48
|
+
error?: string;
|
|
49
|
+
}>;
|
|
50
|
+
/** Evidence map task ID (created if Standard or Critical) */
|
|
51
|
+
evidenceMapTaskId: string | null;
|
|
52
|
+
/** Total estimated tokens for this Ground output */
|
|
53
|
+
estimatedTokens: number;
|
|
54
|
+
/** Ceremony recommendations for this tier */
|
|
55
|
+
ceremony: {
|
|
56
|
+
ground: string;
|
|
57
|
+
build: string;
|
|
58
|
+
break: string;
|
|
59
|
+
evidenceMap: string;
|
|
60
|
+
gate: string;
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
export declare function forgeGround(embedder: IEmbedder, store: IKnowledgeStore, options: ForgeGroundOptions): Promise<ForgeGroundResult>;
|
|
64
|
+
//# sourceMappingURL=forge-ground.d.ts.map
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { evidenceMap } from "./evidence-map.js";
|
|
2
|
+
import { fileSummary } from "./file-summary.js";
|
|
3
|
+
import { forgeClassify } from "./forge-classify.js";
|
|
4
|
+
import { scopeMap } from "./scope-map.js";
|
|
5
|
+
import { estimateTokens } from "./text-utils.js";
|
|
6
|
+
const DEFAULT_MAX_CONSTRAINTS = 3;
|
|
7
|
+
const CONSTRAINT_SNIPPET_CHARS = 200;
|
|
8
|
+
const CEREMONY_BY_TIER = {
|
|
9
|
+
floor: {
|
|
10
|
+
ground: "Parasitic \u2014 read target file only",
|
|
11
|
+
build: "Implement directly",
|
|
12
|
+
break: "Skip",
|
|
13
|
+
evidenceMap: "Not required",
|
|
14
|
+
gate: "Self-certify"
|
|
15
|
+
},
|
|
16
|
+
standard: {
|
|
17
|
+
ground: "Scope map + blast radius + constraint seed",
|
|
18
|
+
build: "TDD \u2014 test first, then implement",
|
|
19
|
+
break: "Error paths + edge cases",
|
|
20
|
+
evidenceMap: "3-8 critical-path entries",
|
|
21
|
+
gate: "YIELD/HOLD evaluation"
|
|
22
|
+
},
|
|
23
|
+
critical: {
|
|
24
|
+
ground: "Full scope map + blast radius + trace + patterns + constraint pack",
|
|
25
|
+
build: "TDD + contract verification + cross-service validation",
|
|
26
|
+
break: "Error paths + edge cases + security dimensions + data-flow verification",
|
|
27
|
+
evidenceMap: "Comprehensive \u2014 all critical-path claims with receipts",
|
|
28
|
+
gate: "Strict YIELD/HOLD/HARD_BLOCK evaluation"
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
async function forgeGround(embedder, store, options) {
|
|
32
|
+
const maxConstraints = options.maxConstraints ?? DEFAULT_MAX_CONSTRAINTS;
|
|
33
|
+
const classifyResult = await classifyTier(options);
|
|
34
|
+
const fileSummaries = await summarizeFiles(options.files);
|
|
35
|
+
if (classifyResult.tier === "floor") {
|
|
36
|
+
return finalizeResult({
|
|
37
|
+
tier: classifyResult.tier,
|
|
38
|
+
classifyTriggers: classifyResult.classifyTriggers,
|
|
39
|
+
scopeMap: null,
|
|
40
|
+
typedUnknownSeeds: classifyResult.typedUnknownSeeds,
|
|
41
|
+
constraints: [],
|
|
42
|
+
fileSummaries,
|
|
43
|
+
evidenceMapTaskId: null,
|
|
44
|
+
ceremony: classifyResult.ceremony
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
const [scopeMapResult, constraints, evidenceMapTaskId] = await Promise.all([
|
|
48
|
+
loadScopeMap(embedder, store, options.task, classifyResult.tier),
|
|
49
|
+
loadConstraints(embedder, store, options.task, maxConstraints),
|
|
50
|
+
createEvidenceMap(
|
|
51
|
+
options.rootPath,
|
|
52
|
+
options.taskId ?? generateTaskId(options.task),
|
|
53
|
+
classifyResult.tier
|
|
54
|
+
)
|
|
55
|
+
]);
|
|
56
|
+
return finalizeResult({
|
|
57
|
+
tier: classifyResult.tier,
|
|
58
|
+
classifyTriggers: classifyResult.classifyTriggers,
|
|
59
|
+
scopeMap: scopeMapResult,
|
|
60
|
+
typedUnknownSeeds: classifyResult.typedUnknownSeeds,
|
|
61
|
+
constraints,
|
|
62
|
+
fileSummaries,
|
|
63
|
+
evidenceMapTaskId,
|
|
64
|
+
ceremony: classifyResult.ceremony
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
async function classifyTier(options) {
|
|
68
|
+
if (options.forceTier) {
|
|
69
|
+
return {
|
|
70
|
+
tier: options.forceTier,
|
|
71
|
+
classifyTriggers: [],
|
|
72
|
+
typedUnknownSeeds: [],
|
|
73
|
+
ceremony: getCeremony(options.forceTier)
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
const result = await forgeClassify({
|
|
78
|
+
files: options.files,
|
|
79
|
+
task: options.task,
|
|
80
|
+
rootPath: options.rootPath
|
|
81
|
+
});
|
|
82
|
+
return {
|
|
83
|
+
tier: result.tier,
|
|
84
|
+
classifyTriggers: result.triggers,
|
|
85
|
+
typedUnknownSeeds: result.typedUnknownSeeds,
|
|
86
|
+
ceremony: result.ceremony
|
|
87
|
+
};
|
|
88
|
+
} catch {
|
|
89
|
+
return {
|
|
90
|
+
tier: "standard",
|
|
91
|
+
classifyTriggers: [],
|
|
92
|
+
typedUnknownSeeds: [],
|
|
93
|
+
ceremony: getCeremony("standard")
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
async function loadScopeMap(embedder, store, task, tier) {
|
|
98
|
+
try {
|
|
99
|
+
return await scopeMap(embedder, store, {
|
|
100
|
+
task,
|
|
101
|
+
maxFiles: tier === "critical" ? 20 : 10
|
|
102
|
+
});
|
|
103
|
+
} catch {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
async function loadConstraints(embedder, store, task, maxConstraints) {
|
|
108
|
+
try {
|
|
109
|
+
const query = `decision pattern convention ${task}`;
|
|
110
|
+
const vector = typeof embedder.embedQuery === "function" ? await embedder.embedQuery(query) : await embedder.embed(query);
|
|
111
|
+
const results = await store.search(vector, {
|
|
112
|
+
limit: maxConstraints,
|
|
113
|
+
origin: "curated"
|
|
114
|
+
});
|
|
115
|
+
return results.slice(0, maxConstraints).map((result) => toConstraintRef(result));
|
|
116
|
+
} catch {
|
|
117
|
+
return [];
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
async function summarizeFiles(files) {
|
|
121
|
+
return Promise.all(files.map(async (path) => summarizeFile(path)));
|
|
122
|
+
}
|
|
123
|
+
async function summarizeFile(path) {
|
|
124
|
+
try {
|
|
125
|
+
const summary = await fileSummary({ path });
|
|
126
|
+
return toGroundFileSummary(summary);
|
|
127
|
+
} catch (error) {
|
|
128
|
+
return {
|
|
129
|
+
path,
|
|
130
|
+
exports: [],
|
|
131
|
+
functions: [],
|
|
132
|
+
lines: 0,
|
|
133
|
+
error: error instanceof Error ? error.message : "Unable to summarize file"
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
async function createEvidenceMap(rootPath, taskId, tier) {
|
|
138
|
+
try {
|
|
139
|
+
evidenceMap({ action: "create", taskId, tier }, rootPath);
|
|
140
|
+
return taskId;
|
|
141
|
+
} catch {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
function toConstraintRef(result) {
|
|
146
|
+
return {
|
|
147
|
+
source: result.record.sourcePath,
|
|
148
|
+
snippet: compactSnippet(result.record.content),
|
|
149
|
+
relevance: result.score
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
function compactSnippet(content) {
|
|
153
|
+
const singleLine = content.replace(/\s+/g, " ").trim();
|
|
154
|
+
if (singleLine.length <= CONSTRAINT_SNIPPET_CHARS) {
|
|
155
|
+
return singleLine;
|
|
156
|
+
}
|
|
157
|
+
return `${singleLine.slice(0, CONSTRAINT_SNIPPET_CHARS - 3).trimEnd()}...`;
|
|
158
|
+
}
|
|
159
|
+
function toGroundFileSummary(summary) {
|
|
160
|
+
return {
|
|
161
|
+
path: summary.path,
|
|
162
|
+
exports: summary.exports,
|
|
163
|
+
functions: summary.functions.map((fn) => fn.name),
|
|
164
|
+
lines: summary.lines
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
function getCeremony(tier) {
|
|
168
|
+
return { ...CEREMONY_BY_TIER[tier] };
|
|
169
|
+
}
|
|
170
|
+
function finalizeResult(result) {
|
|
171
|
+
return {
|
|
172
|
+
...result,
|
|
173
|
+
estimatedTokens: estimateTokens(JSON.stringify(result))
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
function generateTaskId(task) {
|
|
177
|
+
const slug = task.toLowerCase().replace(/[^a-z0-9\s]/g, " ").split(/\s+/).filter(Boolean).slice(0, 5).join("-");
|
|
178
|
+
const ts = Date.now().toString(36);
|
|
179
|
+
return `${slug || "task"}-${ts}`;
|
|
180
|
+
}
|
|
181
|
+
export {
|
|
182
|
+
forgeGround
|
|
183
|
+
};
|
|
184
|
+
//# sourceMappingURL=forge-ground.js.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface GitContextOptions {
|
|
2
|
+
cwd?: string;
|
|
3
|
+
commitCount?: number;
|
|
4
|
+
includeDiff?: boolean;
|
|
5
|
+
}
|
|
6
|
+
export interface GitContextResult {
|
|
7
|
+
branch: string;
|
|
8
|
+
status: {
|
|
9
|
+
staged: string[];
|
|
10
|
+
modified: string[];
|
|
11
|
+
untracked: string[];
|
|
12
|
+
};
|
|
13
|
+
recentCommits: Array<{
|
|
14
|
+
hash: string;
|
|
15
|
+
message: string;
|
|
16
|
+
author: string;
|
|
17
|
+
date: string;
|
|
18
|
+
}>;
|
|
19
|
+
diff?: string;
|
|
20
|
+
}
|
|
21
|
+
export declare function gitContext(options?: GitContextOptions): Promise<GitContextResult>;
|
|
22
|
+
//# sourceMappingURL=git-context.d.ts.map
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
const execFileAsync = promisify(execFile);
|
|
4
|
+
async function git(args, cwd) {
|
|
5
|
+
try {
|
|
6
|
+
const { stdout } = await execFileAsync("git", args, { cwd });
|
|
7
|
+
return stdout.toString().trim();
|
|
8
|
+
} catch {
|
|
9
|
+
return "";
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
async function gitContext(options = {}) {
|
|
13
|
+
const cwd = options.cwd ?? process.cwd();
|
|
14
|
+
const commitCount = options.commitCount ?? 5;
|
|
15
|
+
const [branchRaw, statusRaw, logRaw, diffRaw] = await Promise.all([
|
|
16
|
+
git(["rev-parse", "--abbrev-ref", "HEAD"], cwd),
|
|
17
|
+
git(["status", "--porcelain"], cwd),
|
|
18
|
+
git(["log", `--max-count=${commitCount}`, "--format=%h|%s|%an|%ai"], cwd),
|
|
19
|
+
options.includeDiff ? git(["diff", "--stat", "--no-color"], cwd) : Promise.resolve("")
|
|
20
|
+
]);
|
|
21
|
+
const staged = [];
|
|
22
|
+
const modified = [];
|
|
23
|
+
const untracked = [];
|
|
24
|
+
for (const line of statusRaw.split("\n").filter(Boolean)) {
|
|
25
|
+
const x = line[0];
|
|
26
|
+
const y = line[1];
|
|
27
|
+
const file = line.slice(3).trim();
|
|
28
|
+
if (x !== " " && x !== "?") staged.push(file);
|
|
29
|
+
if (y === "M" || y === "D") modified.push(file);
|
|
30
|
+
if (x === "?") untracked.push(file);
|
|
31
|
+
}
|
|
32
|
+
const recentCommits = logRaw.split("\n").filter(Boolean).map((line) => {
|
|
33
|
+
const [hash, message, author, date] = line.split("|");
|
|
34
|
+
return { hash, message, author, date };
|
|
35
|
+
});
|
|
36
|
+
return {
|
|
37
|
+
branch: branchRaw || "unknown",
|
|
38
|
+
status: { staged, modified, untracked },
|
|
39
|
+
recentCommits,
|
|
40
|
+
diff: diffRaw || void 0
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
export {
|
|
44
|
+
gitContext
|
|
45
|
+
};
|
|
46
|
+
//# sourceMappingURL=git-context.js.map
|