@untangle-code/config 0.0.0-stage → 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.
Files changed (86) hide show
  1. package/LICENSE +661 -0
  2. package/NOTICE +90 -0
  3. package/README.md +262 -2
  4. package/THIRD_PARTY_LICENSES.md +29 -0
  5. package/dist/chunker-budget.d.ts +58 -0
  6. package/dist/chunker-budget.d.ts.map +1 -0
  7. package/dist/chunker-budget.js +11 -0
  8. package/dist/chunker-budget.js.map +1 -0
  9. package/dist/defaults.d.ts +50 -0
  10. package/dist/defaults.d.ts.map +1 -0
  11. package/dist/defaults.js +179 -0
  12. package/dist/defaults.js.map +1 -0
  13. package/dist/discovery.d.ts +40 -0
  14. package/dist/discovery.d.ts.map +1 -0
  15. package/dist/discovery.js +76 -0
  16. package/dist/discovery.js.map +1 -0
  17. package/dist/env.d.ts +37 -0
  18. package/dist/env.d.ts.map +1 -0
  19. package/dist/env.js +100 -0
  20. package/dist/env.js.map +1 -0
  21. package/dist/hash.d.ts +37 -0
  22. package/dist/hash.d.ts.map +1 -0
  23. package/dist/hash.js +52 -0
  24. package/dist/hash.js.map +1 -0
  25. package/dist/index.d.ts +22 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +37 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/load.d.ts +61 -0
  30. package/dist/load.d.ts.map +1 -0
  31. package/dist/load.js +264 -0
  32. package/dist/load.js.map +1 -0
  33. package/dist/merge.d.ts +32 -0
  34. package/dist/merge.d.ts.map +1 -0
  35. package/dist/merge.js +65 -0
  36. package/dist/merge.js.map +1 -0
  37. package/dist/migrations.d.ts +26 -0
  38. package/dist/migrations.d.ts.map +1 -0
  39. package/dist/migrations.js +40 -0
  40. package/dist/migrations.js.map +1 -0
  41. package/dist/parse.d.ts +7 -0
  42. package/dist/parse.d.ts.map +1 -0
  43. package/dist/parse.js +14 -0
  44. package/dist/parse.js.map +1 -0
  45. package/dist/paths.d.ts +14 -0
  46. package/dist/paths.d.ts.map +1 -0
  47. package/dist/paths.js +37 -0
  48. package/dist/paths.js.map +1 -0
  49. package/dist/schema.d.ts +5 -0
  50. package/dist/schema.d.ts.map +1 -0
  51. package/dist/schema.js +19 -0
  52. package/dist/schema.js.map +1 -0
  53. package/dist/secrets.d.ts +33 -0
  54. package/dist/secrets.d.ts.map +1 -0
  55. package/dist/secrets.js +69 -0
  56. package/dist/secrets.js.map +1 -0
  57. package/dist/types.d.ts +323 -0
  58. package/dist/types.d.ts.map +1 -0
  59. package/dist/types.js +20 -0
  60. package/dist/types.js.map +1 -0
  61. package/dist/user-config.d.ts +13 -0
  62. package/dist/user-config.d.ts.map +1 -0
  63. package/dist/user-config.js +34 -0
  64. package/dist/user-config.js.map +1 -0
  65. package/dist/validate.d.ts +36 -0
  66. package/dist/validate.d.ts.map +1 -0
  67. package/dist/validate.js +337 -0
  68. package/dist/validate.js.map +1 -0
  69. package/package.json +65 -3
  70. package/schemas/config-v1.json +375 -0
  71. package/src/chunker-budget.ts +63 -0
  72. package/src/defaults.ts +207 -0
  73. package/src/discovery.ts +95 -0
  74. package/src/env.ts +141 -0
  75. package/src/hash.ts +71 -0
  76. package/src/index.ts +51 -0
  77. package/src/load.ts +346 -0
  78. package/src/merge.ts +69 -0
  79. package/src/migrations.ts +64 -0
  80. package/src/parse.ts +18 -0
  81. package/src/paths.ts +42 -0
  82. package/src/schema.ts +23 -0
  83. package/src/secrets.ts +78 -0
  84. package/src/types.ts +366 -0
  85. package/src/user-config.ts +48 -0
  86. package/src/validate.ts +430 -0
package/dist/schema.js ADDED
@@ -0,0 +1,19 @@
1
+ /**
2
+ * JSON Schema export (architecture.md §6.3, §7.3; docs/BUILD_PLAN.md §3
3
+ * assigns the repo-level `schemas/` directory to this package's WP).
4
+ *
5
+ * NOTE (openIssues): the task that produced this package restricts writes to
6
+ * `packages/config/**`, so the authoritative file lives at
7
+ * `packages/config/schemas/config-v1.json`, not the (currently empty, WP-02
8
+ * owned per docs/BUILD_PLAN.md §4) repo-root `schemas/`. It still needs to be
9
+ * mirrored to `schemas/config-v1.json` by whoever is authorized to write
10
+ * there.
11
+ */
12
+ import { readFileSync } from "node:fs";
13
+ import { fileURLToPath } from "node:url";
14
+ const schemaUrl = new URL("../schemas/config-v1.json", import.meta.url);
15
+ /** Absolute filesystem path to the hand-authored JSON Schema (Draft 2020-12) for `UntangleConfigV1`. */
16
+ export const CONFIG_SCHEMA_PATH = fileURLToPath(schemaUrl);
17
+ /** The parsed JSON Schema object itself, read once at module load. */
18
+ export const CONFIG_SCHEMA = JSON.parse(readFileSync(schemaUrl, "utf8"));
19
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,2BAA2B,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAExE,wGAAwG;AACxG,MAAM,CAAC,MAAM,kBAAkB,GAAW,aAAa,CAAC,SAAS,CAAC,CAAC;AAEnE,sEAAsE;AACtE,MAAM,CAAC,MAAM,aAAa,GAA4B,IAAI,CAAC,KAAK,CAC9D,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,CACL,CAAC"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Secret references and redaction (spec.md §4.4/§4.5 `apiKeyEnv`; §24
3
+ * "API-Keys DÜRFEN nicht in Status, Logs oder Crash Reports erscheinen";
4
+ * architecture.md §23.2: "Der Wert wird nie in Effective Config Dumps
5
+ * geschrieben.").
6
+ *
7
+ * The primary guarantee is structural: `QdrantConfig`/`EmbeddingConfig` only
8
+ * ever carry `apiKeyEnv` — the NAME of an environment variable — never a
9
+ * resolved secret value, so an `EffectiveUntangleConfigV1` cannot contain a
10
+ * secret by construction. `redactSecrets` is a defense-in-depth belt: it
11
+ * scrubs any resolved-secret-shaped field (e.g. code elsewhere mistakenly
12
+ * attaching a resolved key onto a config object for convenience) before that
13
+ * object is serialized, dumped, or logged.
14
+ */
15
+ /**
16
+ * Returns a deep copy of `value` with any field whose name looks like a
17
+ * resolved secret (`apiKey`, `token`, `password`, `secret`, `credential`, but
18
+ * NOT the `...Env` reference fields) replaced by a fixed placeholder. Safe to
19
+ * call on an `EffectiveUntangleConfigV1`, a partial layer, or arbitrary
20
+ * nested data.
21
+ */
22
+ export declare function redactSecrets<T>(value: T): T;
23
+ /** `JSON.stringify` through {@link redactSecrets} — the one safe way to dump a config. */
24
+ export declare function toRedactedJson(value: unknown, space?: number): string;
25
+ /**
26
+ * Resolves an `apiKeyEnv` reference to its actual secret value AT CALL TIME.
27
+ * The result MUST NOT be stored back onto any config object — callers (e.g.
28
+ * a Qdrant/Ollama adapter) use it immediately for one connection attempt.
29
+ * Returns `undefined` when `envVarName` is unset or the variable is not
30
+ * present in `env`.
31
+ */
32
+ export declare function resolveApiKey(envVarName: string | undefined, env?: NodeJS.ProcessEnv): string | undefined;
33
+ //# sourceMappingURL=secrets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secrets.d.ts","sourceRoot":"","sources":["../src/secrets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAiCH;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAE5C;AAED,0FAA0F;AAC1F,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAErE;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,MAAM,GAAG,SAAS,CAKpB"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Secret references and redaction (spec.md §4.4/§4.5 `apiKeyEnv`; §24
3
+ * "API-Keys DÜRFEN nicht in Status, Logs oder Crash Reports erscheinen";
4
+ * architecture.md §23.2: "Der Wert wird nie in Effective Config Dumps
5
+ * geschrieben.").
6
+ *
7
+ * The primary guarantee is structural: `QdrantConfig`/`EmbeddingConfig` only
8
+ * ever carry `apiKeyEnv` — the NAME of an environment variable — never a
9
+ * resolved secret value, so an `EffectiveUntangleConfigV1` cannot contain a
10
+ * secret by construction. `redactSecrets` is a defense-in-depth belt: it
11
+ * scrubs any resolved-secret-shaped field (e.g. code elsewhere mistakenly
12
+ * attaching a resolved key onto a config object for convenience) before that
13
+ * object is serialized, dumped, or logged.
14
+ */
15
+ const REDACTED_PLACEHOLDER = "[REDACTED]";
16
+ // Field name ends in one of these -> looks like a *resolved* secret value.
17
+ const SECRET_VALUE_KEY_PATTERN = /(apikey|secret|password|token|credential)$/i;
18
+ // ...unless it ends in "Env" (e.g. `apiKeyEnv`), which holds an env var NAME
19
+ // and is exactly what spec.md §24 requires to stay visible for transparency.
20
+ const ENV_REFERENCE_SUFFIX_PATTERN = /env$/i;
21
+ function looksLikeResolvedSecretKey(key) {
22
+ if (ENV_REFERENCE_SUFFIX_PATTERN.test(key)) {
23
+ return false;
24
+ }
25
+ return SECRET_VALUE_KEY_PATTERN.test(key);
26
+ }
27
+ function redactRecursive(value) {
28
+ if (Array.isArray(value)) {
29
+ return value.map(redactRecursive);
30
+ }
31
+ if (value !== null && typeof value === "object") {
32
+ const result = {};
33
+ for (const [key, entryValue] of Object.entries(value)) {
34
+ result[key] = looksLikeResolvedSecretKey(key)
35
+ ? REDACTED_PLACEHOLDER
36
+ : redactRecursive(entryValue);
37
+ }
38
+ return result;
39
+ }
40
+ return value;
41
+ }
42
+ /**
43
+ * Returns a deep copy of `value` with any field whose name looks like a
44
+ * resolved secret (`apiKey`, `token`, `password`, `secret`, `credential`, but
45
+ * NOT the `...Env` reference fields) replaced by a fixed placeholder. Safe to
46
+ * call on an `EffectiveUntangleConfigV1`, a partial layer, or arbitrary
47
+ * nested data.
48
+ */
49
+ export function redactSecrets(value) {
50
+ return redactRecursive(value);
51
+ }
52
+ /** `JSON.stringify` through {@link redactSecrets} — the one safe way to dump a config. */
53
+ export function toRedactedJson(value, space) {
54
+ return JSON.stringify(redactSecrets(value), null, space);
55
+ }
56
+ /**
57
+ * Resolves an `apiKeyEnv` reference to its actual secret value AT CALL TIME.
58
+ * The result MUST NOT be stored back onto any config object — callers (e.g.
59
+ * a Qdrant/Ollama adapter) use it immediately for one connection attempt.
60
+ * Returns `undefined` when `envVarName` is unset or the variable is not
61
+ * present in `env`.
62
+ */
63
+ export function resolveApiKey(envVarName, env = process.env) {
64
+ if (envVarName === undefined || envVarName.length === 0) {
65
+ return undefined;
66
+ }
67
+ return env[envVarName];
68
+ }
69
+ //# sourceMappingURL=secrets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secrets.js","sourceRoot":"","sources":["../src/secrets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,oBAAoB,GAAG,YAAY,CAAC;AAE1C,2EAA2E;AAC3E,MAAM,wBAAwB,GAAG,6CAA6C,CAAC;AAC/E,6EAA6E;AAC7E,6EAA6E;AAC7E,MAAM,4BAA4B,GAAG,OAAO,CAAC;AAE7C,SAAS,0BAA0B,CAAC,GAAW;IAC7C,IAAI,4BAA4B,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QAC3C,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,wBAAwB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC5C,CAAC;AAED,SAAS,eAAe,CAAC,KAAc;IACrC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IACpC,CAAC;IACD,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAChD,MAAM,MAAM,GAA4B,EAAE,CAAC;QAC3C,KAAK,MAAM,CAAC,GAAG,EAAE,UAAU,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACtD,MAAM,CAAC,GAAG,CAAC,GAAG,0BAA0B,CAAC,GAAG,CAAC;gBAC3C,CAAC,CAAC,oBAAoB;gBACtB,CAAC,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC;QAClC,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAI,KAAQ;IACvC,OAAO,eAAe,CAAC,KAAK,CAAM,CAAC;AACrC,CAAC;AAED,0FAA0F;AAC1F,MAAM,UAAU,cAAc,CAAC,KAAc,EAAE,KAAc;IAC3D,OAAO,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;AAC3D,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAC3B,UAA8B,EAC9B,MAAyB,OAAO,CAAC,GAAG;IAEpC,IAAI,UAAU,KAAK,SAAS,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxD,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,GAAG,CAAC,UAAU,CAAC,CAAC;AACzB,CAAC"}
@@ -0,0 +1,323 @@
1
+ /**
2
+ * Configuration types (spec.md §4.2–§4.8, §5.7; product.md §11; portfolio.md
3
+ * §14.4; architecture.md §6.3, §23.2).
4
+ *
5
+ * These are the *raw*, *partial*-friendly shapes a `.untangle.json`,
6
+ * environment layer or CLI argument object may contribute. The fully merged,
7
+ * defaulted and validated shape produced by {@link "./load.js"} carries the
8
+ * same interfaces but with all optional fields the product defaults fill in
9
+ * actually present — see `EffectiveUntangleConfigV1` at the bottom of this
10
+ * file.
11
+ */
12
+ import type { ContextArtifactConfig, RepositoryConfig } from "@untangle-code/domain";
13
+ export type { ContextArtifactConfig, ContextArtifactType } from "@untangle-code/domain";
14
+ export type { RepositoryConfig } from "@untangle-code/domain";
15
+ /** spec.md §4.4. */
16
+ export interface QdrantConfig {
17
+ mode: "managed" | "external";
18
+ url?: string;
19
+ /** Name of the environment variable holding the API key — never the key itself (architecture.md §23.2). */
20
+ apiKeyEnv?: string;
21
+ collectionPrefix?: string;
22
+ requestTimeoutMs?: number;
23
+ maxRetries?: number;
24
+ upsertMaxPoints?: number;
25
+ upsertBudgetBytes?: number;
26
+ }
27
+ /** Search backend selection. Relative local paths resolve against the repository root. */
28
+ export interface StorageConfig {
29
+ backend: "lancedb" | "qdrant";
30
+ /** Embedded LanceDB directory; defaults to `.untangle/index/lancedb`. */
31
+ path?: string;
32
+ }
33
+ /** spec.md §4.5. */
34
+ export interface EmbeddingConfig {
35
+ provider: "ollama";
36
+ mode: "auto" | "managed" | "external";
37
+ url?: string;
38
+ /** Name of the environment variable holding the API key — never the key itself (architecture.md §23.2). */
39
+ apiKeyEnv?: string;
40
+ model: string;
41
+ dimensions: number;
42
+ contextLength: number;
43
+ queryPrefix: string;
44
+ documentPrefix: string;
45
+ includePath: boolean;
46
+ batchSize: number;
47
+ maxConnections: number;
48
+ requestTimeoutMs: number;
49
+ }
50
+ /** spec.md §4.6. */
51
+ export interface IndexConfig {
52
+ watch: boolean;
53
+ reconcileIntervalMinutes: number;
54
+ maxFileSizeMb: number;
55
+ maxGraphFileSizeMb: number;
56
+ scanConcurrency: number;
57
+ embeddingConcurrency: number;
58
+ filesPerCheckpoint: number;
59
+ targetChunkTokens: number;
60
+ maxChunkTokens: number;
61
+ fallbackOverlapTokens: number;
62
+ includeDotFiles: boolean;
63
+ indexExtensionless: boolean;
64
+ extraExtensions: string[];
65
+ extensionLanguageMap: Record<string, string>;
66
+ }
67
+ /**
68
+ * Search defaults (Untangle decision — spec.md §13 defines the request/result
69
+ * shapes `SearchRequestV1`/`SearchResultV1` in domain, but names no
70
+ * `SearchConfig` fields; this shape documents which of those request knobs
71
+ * get a project-wide default and adds the boost weights spec.md §13.3 lists
72
+ * without assigning default magnitudes).
73
+ */
74
+ export interface SearchConfig {
75
+ /** Default `limit` when a search request omits it. */
76
+ defaultLimit: number;
77
+ /** Default `candidateLimit` (pre-RRF/pre-filter pool size) when omitted. */
78
+ defaultCandidateLimit: number;
79
+ /** Hard ceiling a caller-supplied `candidateLimit` is clamped to. */
80
+ maxCandidateLimit: number;
81
+ /** Default `diversify` (MMR, architecture.md §18.5) when a request omits it. */
82
+ diversifyByDefault: boolean;
83
+ /** Default `minScore` when a request omits it. */
84
+ minScoreDefault?: number;
85
+ /**
86
+ * Calibrated absence decisions, keyed by the exact embedding profile id.
87
+ *
88
+ * These values are deliberately optional and have no product default: raw
89
+ * cosine scores are model-specific, and their useful separation also
90
+ * depends on the indexed corpus. A policy therefore only applies when its
91
+ * key, workspace id, and physical collection all match the active target.
92
+ */
93
+ relevancePolicies?: Record<string, SearchRelevancePolicyBindingConfigV1>;
94
+ /** Relative weights of the additive score components (spec.md §13.3). */
95
+ boostWeights: {
96
+ exactSymbol: number;
97
+ exactPath: number;
98
+ repoPriority: number;
99
+ graphProximity: number;
100
+ recency: number;
101
+ duplicatePenalty: number;
102
+ };
103
+ }
104
+ /** Corpus binding for a calibrated search relevance policy (WP-231). */
105
+ export interface SearchRelevancePolicyBindingConfigV1 {
106
+ /** Exact logical workspace used while calibrating the thresholds. */
107
+ workspaceId: string;
108
+ /** Exact physical vector collection used while calibrating the thresholds. */
109
+ collection: string;
110
+ policy: SearchRelevancePolicyConfigV1;
111
+ }
112
+ /** Config representation of the search engine's calibrated relevance policy (WP-231). */
113
+ export interface SearchRelevancePolicyConfigV1 {
114
+ /** Human- and machine-readable provenance of the measurements behind the thresholds. */
115
+ calibrationId: string;
116
+ /** Absolute minimum cosine score of the strongest dense candidate, in [-1, 1]. */
117
+ minimumDenseScore: number;
118
+ /** Minimum difference between rank 1 and `denseReferenceRank`, in [0, 2]. */
119
+ minimumDenseSpread: number;
120
+ /** One-based dense rank used for the relative comparison; at least 2. */
121
+ denseReferenceRank: number;
122
+ /** Minimum IDF-weighted fraction of query evidence found together in one candidate. */
123
+ minimumCandidateIdfCoverage: number;
124
+ }
125
+ /**
126
+ * Graph traversal limits (Untangle decision — architecture.md §17.4 names
127
+ * `maxDepth`/`maxNodes`/`maxEdges`/`maxWallTimeMs` as per-query traversal
128
+ * limits without a config shape; this makes them project-configurable
129
+ * defaults). `includeDynamicImports` reflects the `dynamic_import` edge kind
130
+ * from spec.md §14.2 being switchable.
131
+ */
132
+ export interface GraphConfig {
133
+ enabled: boolean;
134
+ maxTraversalDepth: number;
135
+ maxTraversalNodes: number;
136
+ maxTraversalEdges: number;
137
+ maxTraversalWallTimeMs: number;
138
+ includeDynamicImports: boolean;
139
+ }
140
+ /**
141
+ * Complexity engine switches (Untangle decision — spec.md §16 defines the
142
+ * metrics and algorithm IDs exhaustively but no `ComplexityConfig` shape;
143
+ * product.md §11's example config uses `enabled` and `cognitiveAlgorithm`,
144
+ * which anchor this shape). `cyclomaticVariant` selects which of classic
145
+ * (§16.2) and extended (§16.3) get computed; both are cheap to compute
146
+ * together so "both" is the default.
147
+ */
148
+ export interface ComplexityConfig {
149
+ enabled: boolean;
150
+ /** Overridable for future algorithm versions; defaults to `COGNITIVE_ALGORITHM_ID` from domain. */
151
+ cognitiveAlgorithm: string;
152
+ cyclomaticVariant: "classic" | "extended" | "both";
153
+ maintainabilityIndexEnabled: boolean;
154
+ }
155
+ /**
156
+ * Change Intelligence configuration (Untangle decision — spec.md §17.6 gives
157
+ * the default risk score weights and requires every component to be
158
+ * disclosed with its weight and contribution, but does not name a config
159
+ * type). Weights MUST sum to `1.0` within a `±0.001` tolerance, mirroring the
160
+ * validation rule portfolio.md §14.4 states for `blueprint.weights`.
161
+ */
162
+ export interface ChangeConfig {
163
+ enabled: boolean;
164
+ riskWeights: {
165
+ churn: number;
166
+ positiveCognitiveDelta: number;
167
+ positiveCyclomaticDelta: number;
168
+ blastRadius: number;
169
+ graphCentrality: number;
170
+ semanticChange: number;
171
+ testDistance: number;
172
+ };
173
+ symbolMatching: {
174
+ /** Minimum token/MinHash similarity (spec.md §17.3 step 4) to accept a rename/move candidate. */
175
+ tokenSimilarityMin: number;
176
+ /** Whether step 6 (embedding cosine at equal profile) participates at all. */
177
+ enableEmbeddingSimilarity: boolean;
178
+ };
179
+ }
180
+ /** spec.md §4.7. */
181
+ export interface QualityGateConfig {
182
+ mode: "off" | "warn" | "fail";
183
+ newCyclesAllowed: number;
184
+ cognitivePerSymbolMax?: number;
185
+ cognitiveDeltaPerSymbolMax?: number;
186
+ cyclomaticPerSymbolMax?: number;
187
+ cyclomaticDeltaPerSymbolMax?: number;
188
+ maxNestingPerSymbolMax?: number;
189
+ maintainabilityIndexMin?: number;
190
+ riskScoreMax?: number;
191
+ minimumParseQuality: "failed" | "partial" | "complete";
192
+ ignoreExistingViolations: boolean;
193
+ include?: string[];
194
+ exclude?: string[];
195
+ }
196
+ /**
197
+ * Telemetry (spec.md §24: "Telemetrie MUSS standardmäßig aus sein" — telemetry
198
+ * MUST default to off; §23.2/§23.3 name the log/metrics facilities this
199
+ * switches). Shape beyond `enabled` is an Untangle decision.
200
+ */
201
+ export interface TelemetryConfig {
202
+ /** Default `false` — local-first, opt-in only (spec.md §24). */
203
+ enabled: boolean;
204
+ logLevel: "error" | "warn" | "info" | "debug" | "trace";
205
+ /** Whether internal counters (spec.md §23.3) are exported to an external system. */
206
+ metricsExport: "none" | "prometheus";
207
+ }
208
+ /** portfolio.md §13. */
209
+ export interface PortfolioEnrichmentConfig {
210
+ /** Default `false` — no registry/network access without opt-in (portfolio.md §13). */
211
+ enabled: boolean;
212
+ registries: ("npm" | "pypi" | "crates" | "maven" | "nuget" | "go" | "composer")[];
213
+ registryUrls?: Record<string, string>;
214
+ /** Default `168` (one week). */
215
+ cacheTtlHours: number;
216
+ timeoutMs: number;
217
+ }
218
+ /** portfolio.md §14.4 — normative shape. */
219
+ export interface PortfolioConfig {
220
+ /** Default `true`. */
221
+ enabled?: boolean;
222
+ /** Default `false`. */
223
+ includeTransitive?: boolean;
224
+ /** Default `2`. */
225
+ conventionMinRepos?: number;
226
+ conventions?: {
227
+ pinned?: string[];
228
+ suppressed?: string[];
229
+ };
230
+ radar?: {
231
+ /** Default `0.5`. */
232
+ adoptShareMin?: number;
233
+ /** Default `0.2`. */
234
+ maintainShareMin?: number;
235
+ /** Default `540`. */
236
+ retireAfterDays?: number;
237
+ };
238
+ blueprint?: {
239
+ weights?: {
240
+ /** Default `0.40`. */
241
+ adoption?: number;
242
+ /** Default `0.20`. */
243
+ recency?: number;
244
+ /** Default `0.25`. */
245
+ health?: number;
246
+ /** Default `0.15`. */
247
+ versionConsistency?: number;
248
+ };
249
+ };
250
+ enrichment?: PortfolioEnrichmentConfig;
251
+ }
252
+ /**
253
+ * Main configuration schema (spec.md §4.2), extended with `portfolio`
254
+ * (portfolio.md §14.4, folded into `UntangleConfigV1` per portfolio.md
255
+ * §18.1's planned spec.md §4.2 amendment).
256
+ */
257
+ export interface UntangleConfigV1 {
258
+ schemaVersion: 1;
259
+ workspaceId?: string;
260
+ projectId?: string;
261
+ repositories?: RepositoryConfig[];
262
+ storage?: StorageConfig;
263
+ qdrant?: QdrantConfig;
264
+ embeddings?: EmbeddingConfig;
265
+ index?: IndexConfig;
266
+ search?: SearchConfig;
267
+ graph?: GraphConfig;
268
+ complexity?: ComplexityConfig;
269
+ changes?: ChangeConfig;
270
+ qualityGate?: QualityGateConfig;
271
+ contextArtifacts?: ContextArtifactConfig[];
272
+ telemetry?: TelemetryConfig;
273
+ portfolio?: PortfolioConfig;
274
+ }
275
+ /** The set of `UntangleConfigV1` top-level keys, used to detect unknown fields (spec.md §4.8). */
276
+ export declare const UNTANGLE_CONFIG_V1_KEYS: readonly ["schemaVersion", "workspaceId", "projectId", "repositories", "storage", "qdrant", "embeddings", "index", "search", "graph", "complexity", "changes", "qualityGate", "contextArtifacts", "telemetry", "portfolio"];
277
+ /**
278
+ * Recursively makes every field of `T` optional, including inside array
279
+ * elements. Used for {@link RawConfigLayer}: a single JSON file, an env-var
280
+ * layer, or an `explicit` argument object commonly sets only a handful of
281
+ * leaves inside a nested config section (e.g. just `qdrant.collectionPrefix`)
282
+ * — a shallow `Partial<T>` would still require every OTHER field of
283
+ * `QdrantConfig` once `qdrant` is present at all, which is not how any of
284
+ * the six layers in spec.md §4.1 are actually written. Runtime validation
285
+ * (`validate.ts`) is what enforces which fields are truly required once all
286
+ * layers are merged with the product defaults.
287
+ */
288
+ export type DeepPartial<T> = T extends (infer U)[] ? DeepPartial<U>[] : T extends object ? {
289
+ [K in keyof T]?: DeepPartial<T[K]>;
290
+ } : T;
291
+ /**
292
+ * A raw configuration layer as read from JSON, environment mapping or passed
293
+ * explicitly: every field optional (deeply — see {@link DeepPartial}),
294
+ * `schemaVersion` not required (defaults are applied and validated later).
295
+ * `$schema` is accepted and ignored (spec.md §11's example config carries it).
296
+ */
297
+ export type RawConfigLayer = {
298
+ $schema?: string;
299
+ schemaVersion?: number;
300
+ } & DeepPartial<Omit<UntangleConfigV1, "schemaVersion">>;
301
+ /**
302
+ * The fully merged, defaulted and validated configuration
303
+ * ({@link "./load.js"}'s `loadConfig` result). All fields the product
304
+ * defaults (spec.md §4.5, this package's `defaults.ts`) supply are guaranteed
305
+ * present; `repositories` and `contextArtifacts` stay optional because they
306
+ * have no universal default (spec.md §4.3: the current project path MAY
307
+ * serve as an implicit single repository instead).
308
+ */
309
+ export interface EffectiveUntangleConfigV1 extends UntangleConfigV1 {
310
+ schemaVersion: 1;
311
+ storage: StorageConfig;
312
+ qdrant: QdrantConfig;
313
+ embeddings: EmbeddingConfig;
314
+ index: IndexConfig;
315
+ search: SearchConfig;
316
+ graph: GraphConfig;
317
+ complexity: ComplexityConfig;
318
+ changes: ChangeConfig;
319
+ qualityGate: QualityGateConfig;
320
+ telemetry: TelemetryConfig;
321
+ portfolio: PortfolioConfig;
322
+ }
323
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,20 @@
1
+ /** The set of `UntangleConfigV1` top-level keys, used to detect unknown fields (spec.md §4.8). */
2
+ export const UNTANGLE_CONFIG_V1_KEYS = [
3
+ "schemaVersion",
4
+ "workspaceId",
5
+ "projectId",
6
+ "repositories",
7
+ "storage",
8
+ "qdrant",
9
+ "embeddings",
10
+ "index",
11
+ "search",
12
+ "graph",
13
+ "complexity",
14
+ "changes",
15
+ "qualityGate",
16
+ "contextArtifacts",
17
+ "telemetry",
18
+ "portfolio",
19
+ ];
20
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAwSA,kGAAkG;AAClG,MAAM,CAAC,MAAM,uBAAuB,GAAG;IACrC,eAAe;IACf,aAAa;IACb,WAAW;IACX,cAAc;IACd,SAAS;IACT,QAAQ;IACR,YAAY;IACZ,OAAO;IACP,QAAQ;IACR,OAAO;IACP,YAAY;IACZ,SAAS;IACT,aAAa;IACb,kBAAkB;IAClB,WAAW;IACX,WAAW;CAC2C,CAAC"}
@@ -0,0 +1,13 @@
1
+ export declare const USER_CONFIG_FILE_NAME = "config.json";
2
+ /**
3
+ * Resolves the per-OS Untangle config directory:
4
+ * - Linux: `$XDG_CONFIG_HOME/untangle` or `~/.config/untangle`;
5
+ * - macOS: `~/Library/Application Support/untangle`;
6
+ * - Windows: `%APPDATA%\untangle` or `~/AppData/Roaming/untangle`.
7
+ *
8
+ * `env` and `platform` are injectable for deterministic tests.
9
+ */
10
+ export declare function resolveUserConfigDir(env?: NodeJS.ProcessEnv, platform?: NodeJS.Platform): string;
11
+ /** Full path to the user config file, or `undefined` when it does not exist. */
12
+ export declare function resolveUserConfigPath(env?: NodeJS.ProcessEnv, platform?: NodeJS.Platform): string | undefined;
13
+ //# sourceMappingURL=user-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user-config.d.ts","sourceRoot":"","sources":["../src/user-config.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,qBAAqB,gBAAgB,CAAC;AAEnD;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,GAAE,MAAM,CAAC,UAAwB,EACpC,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C,MAAM,CAgBR;AAED,gFAAgF;AAChF,wBAAgB,qBAAqB,CACnC,GAAG,GAAE,MAAM,CAAC,UAAwB,EACpC,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C,MAAM,GAAG,SAAS,CAGpB"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * User configuration location (spec.md §4.1 layer 5: "Nutzerkonfiguration im
3
+ * OS-Konfigurationsverzeichnis"). The exact directory and file name are an
4
+ * Untangle decision — spec.md does not name them.
5
+ */
6
+ import { existsSync } from "node:fs";
7
+ import { homedir } from "node:os";
8
+ import { join } from "node:path";
9
+ export const USER_CONFIG_FILE_NAME = "config.json";
10
+ /**
11
+ * Resolves the per-OS Untangle config directory:
12
+ * - Linux: `$XDG_CONFIG_HOME/untangle` or `~/.config/untangle`;
13
+ * - macOS: `~/Library/Application Support/untangle`;
14
+ * - Windows: `%APPDATA%\untangle` or `~/AppData/Roaming/untangle`.
15
+ *
16
+ * `env` and `platform` are injectable for deterministic tests.
17
+ */
18
+ export function resolveUserConfigDir(env = process.env, platform = process.platform) {
19
+ if (platform === "win32") {
20
+ const appData = env["APPDATA"];
21
+ return join(appData && appData.length > 0 ? appData : join(homedir(), "AppData", "Roaming"), "untangle");
22
+ }
23
+ if (platform === "darwin") {
24
+ return join(homedir(), "Library", "Application Support", "untangle");
25
+ }
26
+ const xdgConfigHome = env["XDG_CONFIG_HOME"];
27
+ return join(xdgConfigHome && xdgConfigHome.length > 0 ? xdgConfigHome : join(homedir(), ".config"), "untangle");
28
+ }
29
+ /** Full path to the user config file, or `undefined` when it does not exist. */
30
+ export function resolveUserConfigPath(env = process.env, platform = process.platform) {
31
+ const candidate = join(resolveUserConfigDir(env, platform), USER_CONFIG_FILE_NAME);
32
+ return existsSync(candidate) ? candidate : undefined;
33
+ }
34
+ //# sourceMappingURL=user-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user-config.js","sourceRoot":"","sources":["../src/user-config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,CAAC,MAAM,qBAAqB,GAAG,aAAa,CAAC;AAEnD;;;;;;;GAOG;AACH,MAAM,UAAU,oBAAoB,CAClC,MAAyB,OAAO,CAAC,GAAG,EACpC,WAA4B,OAAO,CAAC,QAAQ;IAE5C,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,GAAG,CAAC,SAAS,CAAC,CAAC;QAC/B,OAAO,IAAI,CACT,OAAO,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,SAAS,CAAC,EAC/E,UAAU,CACX,CAAC;IACJ,CAAC;IACD,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC1B,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,qBAAqB,EAAE,UAAU,CAAC,CAAC;IACvE,CAAC;IACD,MAAM,aAAa,GAAG,GAAG,CAAC,iBAAiB,CAAC,CAAC;IAC7C,OAAO,IAAI,CACT,aAAa,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,EACtF,UAAU,CACX,CAAC;AACJ,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,qBAAqB,CACnC,MAAyB,OAAO,CAAC,GAAG,EACpC,WAA4B,OAAO,CAAC,QAAQ;IAE5C,MAAM,SAAS,GAAG,IAAI,CAAC,oBAAoB,CAAC,GAAG,EAAE,QAAQ,CAAC,EAAE,qBAAqB,CAAC,CAAC;IACnF,OAAO,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;AACvD,CAAC"}
@@ -0,0 +1,36 @@
1
+ export interface ConfigIssue {
2
+ /** An `ErrorCode` (domain) for errors; a short machine-readable slug for warnings. */
3
+ code: string;
4
+ message: string;
5
+ /** Dotted path into the config object, e.g. `"embeddings.mode"`. */
6
+ path: string;
7
+ }
8
+ export interface ValidationResult {
9
+ errors: ConfigIssue[];
10
+ warnings: ConfigIssue[];
11
+ }
12
+ /**
13
+ * Validates a fully layer-merged (but not yet necessarily complete-with-defaults)
14
+ * configuration object. Returns warnings for unknown top-level fields and
15
+ * errors for invalid enums, unresolvable core values, and structural
16
+ * uniqueness violations (spec.md §4.3 repository ids; portfolio.md §14.4
17
+ * blueprint weight sum).
18
+ */
19
+ export declare function validateEffectiveConfig(config: Record<string, unknown>): ValidationResult;
20
+ /**
21
+ * Throws `UntangleError("CONFIG_INVALID", …)` when `errors` is non-empty
22
+ * (spec.md §4.8: invalid enums / unresolvable core values MUST abort
23
+ * startup). Callers decide when to call this — `loadConfig` itself never
24
+ * throws for either warnings or errors, so a caller can inspect both lists
25
+ * first (e.g. to print them) before deciding to abort.
26
+ */
27
+ export declare function assertConfigValid(errors: readonly ConfigIssue[]): void;
28
+ /**
29
+ * Pre-upsert dimension check (spec.md §4.8: "Falsch konfigurierte Dimensionen
30
+ * MÜSSEN vor dem ersten Index-Upsert erkannt werden."). Callers invoke this
31
+ * with the configured `embeddings.dimensions` and the dimension already
32
+ * committed to an existing Qdrant collection (from `MetadataStore`/
33
+ * `VectorStore` introspection) before ANY upsert into that collection.
34
+ */
35
+ export declare function assertEmbeddingDimensionsMatch(configuredDimensions: number, existingCollectionDimensions: number | undefined): void;
36
+ //# sourceMappingURL=validate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"AAeA,MAAM,WAAW,WAAW;IAC1B,sFAAsF;IACtF,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,oEAAoE;IACpE,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,QAAQ,EAAE,WAAW,EAAE,CAAC;CACzB;AAkJD;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,gBAAgB,CA0MzF;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,GAAG,IAAI,CAatE;AAED;;;;;;GAMG;AACH,wBAAgB,8BAA8B,CAC5C,oBAAoB,EAAE,MAAM,EAC5B,4BAA4B,EAAE,MAAM,GAAG,SAAS,GAC/C,IAAI,CAcN"}