@thinkingsage/kanon 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +410 -0
- package/LICENSE +21 -0
- package/README.md +168 -0
- package/bridge/mcp-server.cjs +14171 -0
- package/package.json +98 -0
- package/src/adapters/capabilities.ts +178 -0
- package/src/adapters/claude-code.ts +110 -0
- package/src/adapters/cline.ts +98 -0
- package/src/adapters/codex.ts +173 -0
- package/src/adapters/copilot.ts +106 -0
- package/src/adapters/cursor.ts +97 -0
- package/src/adapters/degradation.ts +95 -0
- package/src/adapters/index.ts +324 -0
- package/src/adapters/kiro-frontmatter.ts +139 -0
- package/src/adapters/kiro-inclusion.ts +86 -0
- package/src/adapters/kiro.ts +412 -0
- package/src/adapters/qdeveloper.ts +115 -0
- package/src/adapters/types.ts +81 -0
- package/src/adapters/windsurf.ts +96 -0
- package/src/admin.ts +283 -0
- package/src/asset-conventions.ts +118 -0
- package/src/attribution-backfill.ts +319 -0
- package/src/attribution-report.ts +95 -0
- package/src/attribution.ts +239 -0
- package/src/backends/github.ts +194 -0
- package/src/backends/http.ts +122 -0
- package/src/backends/index.ts +39 -0
- package/src/backends/local.ts +47 -0
- package/src/backends/s3.ts +157 -0
- package/src/backends/types.ts +59 -0
- package/src/base-cache.ts +270 -0
- package/src/browse-ui.ts +3754 -0
- package/src/browse.ts +1038 -0
- package/src/build.ts +1108 -0
- package/src/catalog.ts +204 -0
- package/src/cli-deprecated.ts +29 -0
- package/src/cli.ts +773 -0
- package/src/collection-admin.ts +287 -0
- package/src/collection-builder.ts +464 -0
- package/src/collections.ts +116 -0
- package/src/compatibility.ts +105 -0
- package/src/config.ts +743 -0
- package/src/eval/rubrics/kiro-progressive-steering.ts +841 -0
- package/src/eval.ts +1169 -0
- package/src/file-writer.ts +61 -0
- package/src/format-registry.ts +141 -0
- package/src/guild/auto-updater.ts +163 -0
- package/src/guild/backend-resolver.ts +49 -0
- package/src/guild/cli.ts +592 -0
- package/src/guild/collection-expander.ts +47 -0
- package/src/guild/global-cache.ts +247 -0
- package/src/guild/hook-generator.ts +100 -0
- package/src/guild/manifest.ts +154 -0
- package/src/guild/path-utils.ts +12 -0
- package/src/guild/sync.ts +622 -0
- package/src/guild/version-resolver.ts +42 -0
- package/src/help/metadata.ts +445 -0
- package/src/help/renderer.ts +265 -0
- package/src/help/typo-suggester.ts +25 -0
- package/src/hooks/expression.ts +493 -0
- package/src/hooks/pipeline.ts +141 -0
- package/src/import.ts +773 -0
- package/src/importers/claude-code.ts +134 -0
- package/src/importers/cline.ts +103 -0
- package/src/importers/codex.ts +140 -0
- package/src/importers/copilot.ts +103 -0
- package/src/importers/cursor.ts +105 -0
- package/src/importers/index.ts +390 -0
- package/src/importers/kiro.ts +110 -0
- package/src/importers/qdeveloper.ts +103 -0
- package/src/importers/types.ts +54 -0
- package/src/importers/windsurf.ts +104 -0
- package/src/install.ts +1005 -0
- package/src/manifest-admin.ts +306 -0
- package/src/mcp-bridge.ts +240 -0
- package/src/mutation/delta.ts +50 -0
- package/src/mutation/history.ts +66 -0
- package/src/mutation/operators.ts +524 -0
- package/src/mutation/runner.ts +332 -0
- package/src/new.ts +106 -0
- package/src/outcomes/collision.ts +127 -0
- package/src/outcomes/normalize.ts +208 -0
- package/src/outcomes/registry.ts +173 -0
- package/src/parser.ts +446 -0
- package/src/provenance-backfill-cli.ts +319 -0
- package/src/provenance-backfill.ts +520 -0
- package/src/publish.ts +354 -0
- package/src/reconcile-orchestrator.ts +502 -0
- package/src/reconcile-report-renderer.ts +176 -0
- package/src/resolve-body.ts +15 -0
- package/src/rosetta/builtins/compatibility-profiles.ts +297 -0
- package/src/rosetta/builtins/contracts.ts +1033 -0
- package/src/rosetta/builtins/pretty-printers/claude-code-native.ts +122 -0
- package/src/rosetta/builtins/pretty-printers/cline-native.ts +50 -0
- package/src/rosetta/builtins/pretty-printers/codex-native.ts +127 -0
- package/src/rosetta/builtins/pretty-printers/copilot-native.ts +50 -0
- package/src/rosetta/builtins/pretty-printers/cursor-native.ts +50 -0
- package/src/rosetta/builtins/pretty-printers/index.ts +81 -0
- package/src/rosetta/builtins/pretty-printers/kiro-native.ts +166 -0
- package/src/rosetta/builtins/pretty-printers/kiro-power.ts +108 -0
- package/src/rosetta/builtins/pretty-printers/kiro-skill.ts +88 -0
- package/src/rosetta/builtins/pretty-printers/qdeveloper-native.ts +51 -0
- package/src/rosetta/builtins/pretty-printers/superpowers.ts +97 -0
- package/src/rosetta/builtins/pretty-printers/windsurf-native.ts +50 -0
- package/src/rosetta/builtins/sources/claude-code-native.ts +348 -0
- package/src/rosetta/builtins/sources/cline-native.ts +176 -0
- package/src/rosetta/builtins/sources/codex-native.ts +343 -0
- package/src/rosetta/builtins/sources/copilot-native.ts +178 -0
- package/src/rosetta/builtins/sources/cursor-native.ts +176 -0
- package/src/rosetta/builtins/sources/index.ts +95 -0
- package/src/rosetta/builtins/sources/kiro-native.ts +462 -0
- package/src/rosetta/builtins/sources/kiro-power.ts +285 -0
- package/src/rosetta/builtins/sources/kiro-skill.ts +230 -0
- package/src/rosetta/builtins/sources/qdeveloper-native.ts +181 -0
- package/src/rosetta/builtins/sources/superpowers.ts +240 -0
- package/src/rosetta/builtins/sources/windsurf-native.ts +176 -0
- package/src/rosetta/builtins/targets/claude-code.ts +181 -0
- package/src/rosetta/builtins/targets/cline.ts +87 -0
- package/src/rosetta/builtins/targets/codex.ts +226 -0
- package/src/rosetta/builtins/targets/copilot.ts +103 -0
- package/src/rosetta/builtins/targets/cursor.ts +87 -0
- package/src/rosetta/builtins/targets/index.ts +60 -0
- package/src/rosetta/builtins/targets/kiro.ts +278 -0
- package/src/rosetta/builtins/targets/qdeveloper.ts +103 -0
- package/src/rosetta/builtins/targets/windsurf.ts +87 -0
- package/src/rosetta/canonical.ts +729 -0
- package/src/rosetta/compatibility.ts +432 -0
- package/src/rosetta/contracts.ts +329 -0
- package/src/rosetta/detector.ts +724 -0
- package/src/rosetta/diagnostics.ts +630 -0
- package/src/rosetta/engine-bootstrap.ts +103 -0
- package/src/rosetta/engine.ts +744 -0
- package/src/rosetta/index.ts +381 -0
- package/src/rosetta/inspection.ts +530 -0
- package/src/rosetta/plan.ts +448 -0
- package/src/rosetta/provenance-digest.ts +369 -0
- package/src/rosetta/reconcile.ts +812 -0
- package/src/rosetta/redaction.ts +467 -0
- package/src/rosetta/registry.ts +712 -0
- package/src/rosetta/renderers.ts +571 -0
- package/src/rosetta/request-guard.ts +335 -0
- package/src/rosetta/resolution.ts +419 -0
- package/src/rosetta/source-accounting.ts +233 -0
- package/src/rosetta/templates.ts +129 -0
- package/src/rosetta-cli.ts +717 -0
- package/src/rosetta-docs-generator.ts +793 -0
- package/src/rosetta-profiles-cli.ts +367 -0
- package/src/schemas.ts +1712 -0
- package/src/spec-coordination.ts +1141 -0
- package/src/temper.ts +747 -0
- package/src/template-bundle-loader.ts +312 -0
- package/src/template-engine.ts +53 -0
- package/src/translation-application-policy.ts +496 -0
- package/src/translation-orchestrator.ts +1013 -0
- package/src/translation-plan-applier.ts +473 -0
- package/src/tutorial.ts +305 -0
- package/src/validate.ts +1093 -0
- package/src/versioning.ts +553 -0
- package/src/wizard.ts +660 -0
- package/src/workspace.ts +237 -0
- package/templates/eval-contexts/claude-code.md.njk +6 -0
- package/templates/eval-contexts/cline.md.njk +6 -0
- package/templates/eval-contexts/copilot.md.njk +6 -0
- package/templates/eval-contexts/cursor.md.njk +6 -0
- package/templates/eval-contexts/kiro.md.njk +10 -0
- package/templates/eval-contexts/qdeveloper.md.njk +6 -0
- package/templates/eval-contexts/windsurf.md.njk +6 -0
- package/templates/harness-adapters/_base/attribution-footer.md.njk +17 -0
- package/templates/harness-adapters/_base/base.md.njk +16 -0
- package/templates/harness-adapters/claude-code/claude.md.njk +1 -0
- package/templates/harness-adapters/claude-code/mcp.json.njk +1 -0
- package/templates/harness-adapters/claude-code/settings.json.njk +1 -0
- package/templates/harness-adapters/claude-code/skill-library-index.md.njk +13 -0
- package/templates/harness-adapters/claude-code/skill.md.njk +19 -0
- package/templates/harness-adapters/cline/hook.sh.njk +4 -0
- package/templates/harness-adapters/cline/mcp.json.njk +1 -0
- package/templates/harness-adapters/cline/rule.md.njk +1 -0
- package/templates/harness-adapters/codex/agents-md.md.njk +6 -0
- package/templates/harness-adapters/codex/agents-pointer.md.njk +16 -0
- package/templates/harness-adapters/codex/skill.md.njk +27 -0
- package/templates/harness-adapters/copilot/agents.md.njk +1 -0
- package/templates/harness-adapters/copilot/instructions.md.njk +1 -0
- package/templates/harness-adapters/copilot/scoped.md.njk +6 -0
- package/templates/harness-adapters/cursor/mcp.json.njk +1 -0
- package/templates/harness-adapters/cursor/rule.md.njk +6 -0
- package/templates/harness-adapters/kiro/hook.json.njk +1 -0
- package/templates/harness-adapters/kiro/mcp.json.njk +1 -0
- package/templates/harness-adapters/kiro/power-steering.md.njk +3 -0
- package/templates/harness-adapters/kiro/power.md.njk +12 -0
- package/templates/harness-adapters/kiro/steering.md.njk +16 -0
- package/templates/harness-adapters/qdeveloper/agent.md.njk +1 -0
- package/templates/harness-adapters/qdeveloper/mcp.json.njk +1 -0
- package/templates/harness-adapters/qdeveloper/rule.md.njk +1 -0
- package/templates/harness-adapters/windsurf/mcp.json.njk +1 -0
- package/templates/harness-adapters/windsurf/rule.md.njk +1 -0
- package/templates/harness-adapters/windsurf/workflow.md.njk +1 -0
- package/templates/knowledge/hooks.yaml.njk +4 -0
- package/templates/knowledge/knowledge.md.njk +53 -0
- package/templates/knowledge/mcp-servers.yaml.njk +2 -0
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base-Artifact Cache and Import-Time Provenance (Orchestration Shell)
|
|
3
|
+
*
|
|
4
|
+
* ORCHESTRATION LAYER — this module performs filesystem IO and reads the clock,
|
|
5
|
+
* so it deliberately lives OUTSIDE `src/rosetta/**` (the Pure_Translation_Boundary
|
|
6
|
+
* enforced by `rosetta-architecture-boundary.test.ts`). It consumes the pure
|
|
7
|
+
* Rosetta Stone helpers rather than duplicating them:
|
|
8
|
+
*
|
|
9
|
+
* - `computeBaseDigest` (src/rosetta/provenance-digest.ts) computes the
|
|
10
|
+
* deterministic `sha256:<hex>` fingerprint of the normalized Theirs_Artifact.
|
|
11
|
+
* - `serializeCanonical` (src/rosetta/canonical.ts) renders the deterministic
|
|
12
|
+
* canonical byte plan that is written to the base cache.
|
|
13
|
+
*
|
|
14
|
+
* Two responsibilities, both invoked by the import/acquisition path
|
|
15
|
+
* (ADR-0049 / ADR-RS-007):
|
|
16
|
+
*
|
|
17
|
+
* 1. `buildProvenanceRecord` — a PURE builder that assembles a
|
|
18
|
+
* `ProvenanceRecord` from the acquired revision plus a computed
|
|
19
|
+
* `baseDigest`. It takes `importedAt` as an explicit argument so the clock
|
|
20
|
+
* stays in the orchestration caller (Requirement 18.1).
|
|
21
|
+
*
|
|
22
|
+
* 2. `writeBaseArtifact` — the impure cache writer. It serializes the
|
|
23
|
+
* normalized Base_Artifact and writes it to a git-ignored cache at
|
|
24
|
+
* `upstream/.kanon-base/<upstream>/<name>@<digest>` so that a later re-sync
|
|
25
|
+
* can reconstruct the common ancestor for three-way reconciliation
|
|
26
|
+
* (Requirement 18.2). On a cache miss the reconciler degrades to a two-way
|
|
27
|
+
* merge; this writer is what populates the cache to avoid that degradation.
|
|
28
|
+
*
|
|
29
|
+
* Requirements: 18.1, 18.2
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
33
|
+
import { dirname, join } from "node:path";
|
|
34
|
+
import type { CanonicalSerializerOptions } from "./rosetta/canonical";
|
|
35
|
+
import { serializeCanonical } from "./rosetta/canonical";
|
|
36
|
+
import { computeBaseDigest } from "./rosetta/provenance-digest";
|
|
37
|
+
import type {
|
|
38
|
+
FormatIdentifier,
|
|
39
|
+
KnowledgeArtifact,
|
|
40
|
+
ProvenanceRecord,
|
|
41
|
+
} from "./schemas";
|
|
42
|
+
|
|
43
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
44
|
+
// Constants
|
|
45
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The git-ignored root, relative to the repository/workspace root, under which
|
|
49
|
+
* normalized Base_Artifact content is cached. Matches the path decided in
|
|
50
|
+
* ADR-0049 and the Rosetta Stone design's "Base reconstruction" section.
|
|
51
|
+
*/
|
|
52
|
+
export const BASE_CACHE_ROOT = join("upstream", ".kanon-base");
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Serializer options used for both the digest and the cached content, so that
|
|
56
|
+
* the bytes hashed at import time match the bytes written to the cache and the
|
|
57
|
+
* bytes a later re-sync will re-hash. These mirror the options the import path
|
|
58
|
+
* uses when it serializes the artifact for writing.
|
|
59
|
+
*/
|
|
60
|
+
const BASE_CACHE_SERIALIZER_OPTIONS: CanonicalSerializerOptions = {
|
|
61
|
+
emitEmptyAuxiliaryFiles: true,
|
|
62
|
+
emitBodyOverrides: true,
|
|
63
|
+
emitWorkflows: true,
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
67
|
+
// Provenance builder (pure)
|
|
68
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* The acquired-revision context an acquisition-driven import supplies so a
|
|
72
|
+
* `ProvenanceRecord` can be populated. Every field is caller-provided because
|
|
73
|
+
* translation is pure and must not read Git, the clock, or the filesystem
|
|
74
|
+
* implicitly (Requirement 12.6/12.7).
|
|
75
|
+
*/
|
|
76
|
+
export interface AcquisitionContext {
|
|
77
|
+
/** The upstream identifier — matches a key in config `upstreams`. */
|
|
78
|
+
readonly upstream: string;
|
|
79
|
+
/** The source subpath within the upstream repository. */
|
|
80
|
+
readonly sourcePath: string;
|
|
81
|
+
/** The Source_Format the artifact was distilled from. */
|
|
82
|
+
readonly sourceFormat: FormatIdentifier;
|
|
83
|
+
/** The upstream revision (subtree commit) the import was taken from. */
|
|
84
|
+
readonly sourceRevision: string;
|
|
85
|
+
/**
|
|
86
|
+
* The Rosetta Stone Format_Contract identifier and version, e.g.
|
|
87
|
+
* `kiro-power@1`.
|
|
88
|
+
*/
|
|
89
|
+
readonly contract: string;
|
|
90
|
+
/**
|
|
91
|
+
* The import timestamp as an ISO-8601 string, supplied by the orchestrator
|
|
92
|
+
* so this builder stays pure.
|
|
93
|
+
*/
|
|
94
|
+
readonly importedAt: string;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* The result of building a `ProvenanceRecord`: the record when the artifact
|
|
99
|
+
* could be fingerprinted, or `undefined` with the serializer diagnostics that
|
|
100
|
+
* prevented digest computation.
|
|
101
|
+
*/
|
|
102
|
+
export interface ProvenanceBuildResult {
|
|
103
|
+
/** The populated provenance record, or undefined when the digest failed. */
|
|
104
|
+
readonly provenance: ProvenanceRecord | undefined;
|
|
105
|
+
/** Serializer diagnostics surfaced during digest computation (empty on success). */
|
|
106
|
+
readonly diagnostics: ReturnType<typeof computeBaseDigest>["diagnostics"];
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Build a `ProvenanceRecord` from the acquired revision and the freshly
|
|
111
|
+
* translated Theirs_Artifact.
|
|
112
|
+
*
|
|
113
|
+
* The `baseDigest` is computed by the pure `computeBaseDigest` helper over the
|
|
114
|
+
* normalized artifact, so identical upstream revisions and translation options
|
|
115
|
+
* yield an identical digest (Requirement 18.2). When the artifact cannot be
|
|
116
|
+
* serialized (an invalid candidate), no digest — and therefore no provenance —
|
|
117
|
+
* is produced, and the serializer diagnostics are returned instead of writing a
|
|
118
|
+
* malformed record.
|
|
119
|
+
*
|
|
120
|
+
* Pure: depends only on its arguments. The clock value arrives via
|
|
121
|
+
* `context.importedAt`.
|
|
122
|
+
*
|
|
123
|
+
* @param artifact The freshly translated Theirs_Artifact being distilled.
|
|
124
|
+
* @param context The acquired-revision context.
|
|
125
|
+
* @returns The provenance record (or undefined) plus any digest diagnostics.
|
|
126
|
+
*/
|
|
127
|
+
export function buildProvenanceRecord(
|
|
128
|
+
artifact: KnowledgeArtifact,
|
|
129
|
+
context: AcquisitionContext,
|
|
130
|
+
): ProvenanceBuildResult {
|
|
131
|
+
const { digest, diagnostics } = computeBaseDigest(
|
|
132
|
+
artifact,
|
|
133
|
+
BASE_CACHE_SERIALIZER_OPTIONS,
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
if (digest === undefined) {
|
|
137
|
+
return { provenance: undefined, diagnostics };
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const provenance: ProvenanceRecord = {
|
|
141
|
+
upstream: context.upstream,
|
|
142
|
+
sourcePath: context.sourcePath,
|
|
143
|
+
sourceFormat: context.sourceFormat,
|
|
144
|
+
sourceRevision: context.sourceRevision,
|
|
145
|
+
contract: context.contract,
|
|
146
|
+
baseDigest: digest,
|
|
147
|
+
importedAt: context.importedAt,
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
return { provenance, diagnostics: [] };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
154
|
+
// Base-artifact cache path (pure)
|
|
155
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* The digest embeds a `sha256:` prefix and a hex body. `:` is unsafe as a path
|
|
159
|
+
* segment on some filesystems, so it is replaced with `-` when forming the
|
|
160
|
+
* `<name>@<digest>` cache key. The stored key remains a stable, reversible
|
|
161
|
+
* function of the digest.
|
|
162
|
+
*/
|
|
163
|
+
function digestToPathSegment(digest: string): string {
|
|
164
|
+
return digest.replace(/:/g, "-");
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Compute the git-ignored cache path for a Base_Artifact, relative to the
|
|
169
|
+
* provided workspace root:
|
|
170
|
+
* `upstream/.kanon-base/<upstream>/<name>@<digest>` (ADR-0049).
|
|
171
|
+
*
|
|
172
|
+
* Pure: derives a path string only; performs no IO.
|
|
173
|
+
*
|
|
174
|
+
* @param workspaceRoot The root the cache is anchored under.
|
|
175
|
+
* @param upstream The upstream identifier.
|
|
176
|
+
* @param name The canonical artifact name.
|
|
177
|
+
* @param digest The `sha256:<hex>` Base_Digest.
|
|
178
|
+
* @returns The absolute cache directory path for this base artifact.
|
|
179
|
+
*/
|
|
180
|
+
export function baseArtifactCachePath(
|
|
181
|
+
workspaceRoot: string,
|
|
182
|
+
upstream: string,
|
|
183
|
+
name: string,
|
|
184
|
+
digest: string,
|
|
185
|
+
): string {
|
|
186
|
+
return join(
|
|
187
|
+
workspaceRoot,
|
|
188
|
+
BASE_CACHE_ROOT,
|
|
189
|
+
upstream,
|
|
190
|
+
`${name}@${digestToPathSegment(digest)}`,
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
195
|
+
// Base-artifact cache writer (impure)
|
|
196
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* The outcome of writing a Base_Artifact to the cache.
|
|
200
|
+
*/
|
|
201
|
+
export interface BaseCacheWriteResult {
|
|
202
|
+
/** True when the normalized base content was written. */
|
|
203
|
+
readonly written: boolean;
|
|
204
|
+
/** The cache directory the content was written to, when written. */
|
|
205
|
+
readonly cachePath: string | undefined;
|
|
206
|
+
/** Serializer diagnostics that prevented the write (empty on success). */
|
|
207
|
+
readonly diagnostics: ReturnType<typeof serializeCanonical>["diagnostics"];
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Serialize and write the normalized Base_Artifact content to the git-ignored
|
|
212
|
+
* base cache (Requirement 18.2).
|
|
213
|
+
*
|
|
214
|
+
* The artifact is serialized with the same options used to compute its
|
|
215
|
+
* `baseDigest`, so the cached bytes reconstruct exactly the artifact the digest
|
|
216
|
+
* fingerprints. The content is written to
|
|
217
|
+
* `upstream/.kanon-base/<upstream>/<name>@<digest>/<file>` for every file in
|
|
218
|
+
* the canonical plan, preserving the canonical directory layout so a re-sync can
|
|
219
|
+
* re-parse the cached tree into a Base_Artifact.
|
|
220
|
+
*
|
|
221
|
+
* Impure: creates directories and writes files. On a serialization failure it
|
|
222
|
+
* writes nothing and returns the diagnostics so the caller can decide to
|
|
223
|
+
* proceed without a cached base (the reconciler then degrades to a two-way
|
|
224
|
+
* merge).
|
|
225
|
+
*
|
|
226
|
+
* @param artifact The normalized Base_Artifact to cache.
|
|
227
|
+
* @param digest The `sha256:<hex>` Base_Digest keying the cache entry.
|
|
228
|
+
* @param context The acquisition context (supplies the upstream identifier).
|
|
229
|
+
* @param workspaceRoot The root the cache is anchored under.
|
|
230
|
+
* @returns Whether the content was written, the cache path, and diagnostics.
|
|
231
|
+
*/
|
|
232
|
+
export async function writeBaseArtifact(
|
|
233
|
+
artifact: KnowledgeArtifact,
|
|
234
|
+
digest: string,
|
|
235
|
+
context: Pick<AcquisitionContext, "upstream">,
|
|
236
|
+
workspaceRoot: string,
|
|
237
|
+
): Promise<BaseCacheWriteResult> {
|
|
238
|
+
const { plan, diagnostics } = serializeCanonical(
|
|
239
|
+
artifact,
|
|
240
|
+
BASE_CACHE_SERIALIZER_OPTIONS,
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
if (!plan) {
|
|
244
|
+
return { written: false, cachePath: undefined, diagnostics };
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const cachePath = baseArtifactCachePath(
|
|
248
|
+
workspaceRoot,
|
|
249
|
+
context.upstream,
|
|
250
|
+
artifact.name,
|
|
251
|
+
digest,
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
await mkdir(cachePath, { recursive: true });
|
|
255
|
+
|
|
256
|
+
for (const file of plan.outputFiles) {
|
|
257
|
+
const destPath = join(cachePath, file.relativePath);
|
|
258
|
+
const parent = dirname(destPath);
|
|
259
|
+
if (parent !== cachePath) {
|
|
260
|
+
await mkdir(parent, { recursive: true });
|
|
261
|
+
}
|
|
262
|
+
const content =
|
|
263
|
+
typeof file.content === "string"
|
|
264
|
+
? file.content
|
|
265
|
+
: new TextDecoder().decode(file.content);
|
|
266
|
+
await writeFile(destPath, content, "utf-8");
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return { written: true, cachePath, diagnostics: [] };
|
|
270
|
+
}
|