session-orchestrator 4.0.1 → 4.2.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/.agents/skills/session-plan/SKILL.md +1 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.codex-plugin/skills/session-plan/SKILL.md +1 -1
- package/.cursor/skills/session-plan/SKILL.md +1 -1
- package/.cursor-plugin/plugin.json +1 -1
- package/CHANGELOG.md +57 -0
- package/README.md +55 -51
- package/agents/ux-evaluator.md +1 -1
- package/commands/close.md +3 -3
- package/commands/go.md +2 -0
- package/commands/memory-cleanup.md +4 -3
- package/commands/persona-panel.md +1 -1
- package/commands/release.md +4 -4
- package/commands/session.md +3 -2
- package/docs/README.md +4 -4
- package/docs/USER-GUIDE.md +115 -48
- package/docs/agent-authoring.md +2 -2
- package/docs/baseline.md +55 -1
- package/docs/ci-setup.md +1 -1
- package/docs/codex-setup.md +1 -0
- package/docs/components.md +2 -2
- package/docs/cursor-setup.md +1 -0
- package/docs/events-schema.md +4 -1
- package/docs/instruction-delivery.md +1 -1
- package/docs/memory-proposal-flow.md +3 -3
- package/docs/migration-v4.md +2 -2
- package/docs/owner-config-schema.md +74 -90
- package/docs/persona-panel.md +4 -4
- package/docs/pi-setup.md +1 -0
- package/docs/rule-authoring.md +13 -6
- package/docs/scope-collision-guard.md +2 -0
- package/docs/session-config-reference.md +55 -22
- package/docs/session-config-template.md +9 -5
- package/docs/vault-docs-architecture.md +4 -2
- package/hooks/_lib/hook-import-set.json +28 -3
- package/hooks/_lib/vcs-create-matcher.mjs +214 -16
- package/hooks/hooks-codex.json +1 -1
- package/hooks/hooks.json +1 -1
- package/hooks/pre-bash-issue-budget.mjs +123 -26
- package/hooks/subagent-telemetry.mjs +106 -20
- package/package.json +4 -4
- package/scripts/baseline-archetypes.mjs +28 -0
- package/scripts/ci/assert-coverage-green.mjs +100 -0
- package/scripts/lib/auto-dialectic.mjs +0 -68
- package/scripts/lib/baseline-archetypes.mjs +439 -0
- package/scripts/lib/build-live-signals.mjs +5 -6
- package/scripts/lib/config/issue-budget.mjs +68 -8
- package/scripts/lib/config/private-config-dir.mjs +3 -2
- package/scripts/lib/config/remote-hosts.mjs +2 -2
- package/scripts/lib/config-schema.mjs +79 -0
- package/scripts/lib/events.mjs +3 -3
- package/scripts/lib/file-lock.mjs +47 -5
- package/scripts/lib/issue-budget-reconcile.mjs +392 -0
- package/scripts/lib/issue-budget.mjs +76 -3
- package/scripts/lib/learnings/evolve-telemetry.mjs +1 -2
- package/scripts/lib/maintenance-due-banner.mjs +440 -0
- package/scripts/lib/owner-config.example.yaml +29 -46
- package/scripts/lib/owner-yaml.mjs +14 -13
- package/scripts/lib/project-hygiene.mjs +182 -6
- package/scripts/lib/quality-gate.mjs +13 -6
- package/scripts/lib/resource-probe/evaluate.mjs +19 -21
- package/scripts/lib/rules-sync.mjs +34 -4
- package/scripts/lib/session-close-backfill.mjs +182 -40
- package/scripts/lib/session-end/phase-skip.mjs +85 -86
- package/scripts/lib/session-end/tail-runner.mjs +178 -0
- package/scripts/lib/session-identity/own-session.mjs +24 -13
- package/scripts/lib/session-schema/constants.mjs +6 -0
- package/scripts/lib/session-schema/validator.mjs +20 -0
- package/scripts/lib/session-shape.mjs +558 -0
- package/scripts/lib/session-start-probes.mjs +10 -3
- package/scripts/lib/session-token-rollup.mjs +95 -10
- package/scripts/lib/state-md/frontmatter-mutators.mjs +22 -34
- package/scripts/lib/state-md.mjs +1 -0
- package/scripts/lib/subagents-schema.mjs +77 -9
- package/scripts/lib/telemetry/pricing.mjs +197 -0
- package/scripts/lib/telemetry/sync.mjs +50 -1
- package/scripts/lib/validate/check-owner-leakage.mjs +17 -8
- package/scripts/lib/validate/check-skill-script-paths.mjs +33 -10
- package/scripts/lib/validate/check-unwired-features.mjs +8 -7
- package/scripts/lib/vault-mirror/process.mjs +2 -1
- package/scripts/lib/vault-mirror/render-sessions.mjs +8 -1
- package/scripts/lib/vault-status/narrative-mirror.mjs +4 -4
- package/scripts/lib/wave-resource-gate.mjs +23 -27
- package/scripts/lib/wave-sizing.mjs +10 -3
- package/scripts/materialize-wave-scope.mjs +68 -14
- package/scripts/print-applicable-rules.mjs +7 -6
- package/scripts/print-learnings-index.mjs +3 -2
- package/scripts/release.mjs +32 -11
- package/scripts/session-shape.mjs +266 -0
- package/skills/_shared/config-reading.md +15 -9
- package/skills/_shared/private-capability-context.md +89 -0
- package/skills/bootstrap/SKILL.md +61 -13
- package/skills/bootstrap/_shared-template.md +99 -14
- package/skills/bootstrap/deep-template.md +36 -26
- package/skills/bootstrap/fast-template.md +44 -8
- package/skills/bootstrap/intensity-heuristic.md +10 -4
- package/skills/bootstrap/private-contract.md +119 -0
- package/skills/bootstrap/public-fallback.md +30 -18
- package/skills/bootstrap/standard-template.md +39 -24
- package/skills/discovery/probes-ui.md +1 -1
- package/skills/docs-orchestrator/audience-mapping.md +1 -1
- package/skills/evolve/SKILL.md +2 -2
- package/skills/gitlab-ops/SKILL.md +3 -3
- package/skills/grill/SKILL.md +1 -1
- package/skills/memory-cleanup/SKILL.md +2 -2
- package/skills/plan/mode-new.md +9 -0
- package/skills/reconcile/SKILL.md +1 -1
- package/skills/session-end/SKILL.md +3 -2
- package/skills/session-end/phase-3-2-docs-verification.md +1 -1
- package/skills/session-end/phase-3-6-tail.md +23 -65
- package/skills/session-end/phase-3-7a-recommendations.md +2 -2
- package/skills/session-end/references/phase-3-documentation-updates.md +8 -6
- package/skills/session-end/references/phase-5-issue-cleanup.md +26 -0
- package/skills/session-end/session-metrics-write.md +31 -12
- package/skills/session-plan/SKILL.md +56 -48
- package/skills/session-plan/wave-template.md +8 -15
- package/skills/session-start/SKILL.md +18 -2
- package/skills/session-start/phase-2-5-docs-planning.md +1 -1
- package/skills/session-start/phase-8-5-express-path.md +12 -9
- package/skills/session-start/references/phase-1-5-session-continuity.md +2 -0
- package/skills/session-start/references/phase-4-ssot-environment-check.md +21 -5
- package/skills/session-start/references/phase-6-7-memory-banner-telemetry-consent.md +3 -1
- package/skills/test-runner/rubric-v1.md +2 -2
- package/skills/wave-executor/SKILL.md +42 -12
- package/skills/wave-executor/circuit-breaker.md +3 -1
- package/skills/wave-executor/references/wave-loop-dispatch.md +4 -2
- package/skills/wave-executor/references/wave-loop-review.md +1 -1
- package/skills/wave-executor/references/wave-loop-scope-manifest.md +6 -2
- package/templates/nextjs-minimal/package.json +1 -1
- package/templates/node-minimal/package.json +1 -1
- package/scripts/lib/multi-provider-build/providers.mjs +0 -64
- package/scripts/lib/multi-provider-build/templating.mjs +0 -130
- package/scripts/lib/owner-config/coerce.mjs +0 -29
- package/scripts/lib/owner-config/constants.mjs +0 -21
- package/scripts/lib/owner-config/defaults.mjs +0 -50
- package/scripts/lib/owner-config/error.mjs +0 -19
- package/scripts/lib/owner-config/index.mjs +0 -13
- package/scripts/lib/owner-config/merge.mjs +0 -52
- package/scripts/lib/owner-config/validate.mjs +0 -259
- package/scripts/lib/owner-config-loader.mjs +0 -170
- package/scripts/lib/owner-config.mjs +0 -28
- package/scripts/lib/soul-resolve.mjs +0 -130
- package/scripts/lib/vault-mirror/render.mjs +0 -8
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* owner-config-loader.mjs — File-system loader for owner.yaml (Issue #174).
|
|
3
|
-
*
|
|
4
|
-
* Companion to the pure schema/validator in `owner-config.mjs`. Resolves the
|
|
5
|
-
* canonical config path (XDG-spec aware), reads the YAML, parses it, runs it
|
|
6
|
-
* through the validator, and returns a `{ok, value, errors, source, path}`
|
|
7
|
-
* envelope.
|
|
8
|
-
*
|
|
9
|
-
* ── PATH RESOLUTION ──────────────────────────────────────────────────────
|
|
10
|
-
*
|
|
11
|
-
* Default path (#1223 — delegated to the single resolver
|
|
12
|
-
* `config/private-config-dir.mjs`; `SO_CONFIG_HOME` names the private dir
|
|
13
|
-
* ITSELF and wins):
|
|
14
|
-
* `${SO_CONFIG_HOME ?? ${XDG_CONFIG_HOME ?? ${HOME}/.config}/session-orchestrator}/owner.yaml`
|
|
15
|
-
* - macOS / Linux: `~/.config/session-orchestrator/owner.yaml`
|
|
16
|
-
* - Windows: `%APPDATA%\session-orchestrator\owner.yaml` when
|
|
17
|
-
* APPDATA is set and XDG_CONFIG_HOME is not (Node uses
|
|
18
|
-
* USERPROFILE for os.homedir() — falls back gracefully).
|
|
19
|
-
*
|
|
20
|
-
* Callers may pass an explicit `{path}` override (used by tests + D4
|
|
21
|
-
* baseline-propagation when the canonical path is overridden by env).
|
|
22
|
-
*
|
|
23
|
-
* ── RETURN SHAPE ─────────────────────────────────────────────────────────
|
|
24
|
-
*
|
|
25
|
-
* {
|
|
26
|
-
* ok: boolean, // false on any failure
|
|
27
|
-
* value: object|null, // normalized config when ok=true
|
|
28
|
-
* errors: string[], // populated on any failure
|
|
29
|
-
* source: 'file'|'missing'|'parse-error'|'validation-error',
|
|
30
|
-
* path: string, // resolved absolute path
|
|
31
|
-
* }
|
|
32
|
-
*
|
|
33
|
-
* `source: 'missing'` is NOT an error — owner.yaml is opt-in (the bootstrap
|
|
34
|
-
* interview in D2 creates it). Callers should treat `source: 'missing'` as
|
|
35
|
-
* "no owner persona configured; fall back to plugin defaults".
|
|
36
|
-
*
|
|
37
|
-
* ── PLATFORM PORTABILITY ─────────────────────────────────────────────────
|
|
38
|
-
*
|
|
39
|
-
* - Uses `os.homedir()` (Node-stdlib, OS-aware) rather than $HOME so the
|
|
40
|
-
* Windows path resolves correctly even when only USERPROFILE is set.
|
|
41
|
-
* - File reads are UTF-8 only. Owner.yaml is ASCII-safe by spec; non-UTF-8
|
|
42
|
-
* content yields `source: 'parse-error'`.
|
|
43
|
-
* - YAML parsing via `js-yaml` (devDep already pulled in by W2 quality-fix).
|
|
44
|
-
*/
|
|
45
|
-
|
|
46
|
-
import { readFile } from 'node:fs/promises';
|
|
47
|
-
import { existsSync } from 'node:fs';
|
|
48
|
-
import { join } from 'node:path';
|
|
49
|
-
import yaml from 'js-yaml';
|
|
50
|
-
|
|
51
|
-
import { validate } from './owner-config.mjs';
|
|
52
|
-
import { resolvePrivateConfigDir } from './config/private-config-dir.mjs';
|
|
53
|
-
|
|
54
|
-
// ---------------------------------------------------------------------------
|
|
55
|
-
// Path resolution
|
|
56
|
-
// ---------------------------------------------------------------------------
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Resolve the canonical owner.yaml path on disk: the ONE private-config-dir
|
|
60
|
-
* resolver (#1223) plus the file's basename — precedence `SO_CONFIG_HOME` (the
|
|
61
|
-
* private dir itself) > `XDG_CONFIG_HOME` (its parent) > `${HOME}/.config`, each
|
|
62
|
-
* `.trim()`ed so a whitespace-only value falls through instead of
|
|
63
|
-
* short-circuiting (`.claude/rules/development.md` § Error Handling).
|
|
64
|
-
*
|
|
65
|
-
* Reaches the leaf DIRECTLY rather than through `owner-yaml.mjs`'s
|
|
66
|
-
* `resolveOwnerYamlPath()`: that hop exists only to append the same basename and
|
|
67
|
-
* drags a `js-yaml`-importing module in with it. The precedence — the part #1223
|
|
68
|
-
* consolidated — still lives in exactly one place; only the literal `'owner.yaml'`
|
|
69
|
-
* is stated twice.
|
|
70
|
-
*
|
|
71
|
-
* Before #1223 this function knew only `XDG_CONFIG_HOME` and did not trim, so
|
|
72
|
-
* `SO_CONFIG_HOME=<sandbox>` still read the operator's real home.
|
|
73
|
-
*
|
|
74
|
-
* Returns an absolute path string. Does NOT touch the filesystem.
|
|
75
|
-
*
|
|
76
|
-
* @returns {string} absolute path to owner.yaml
|
|
77
|
-
*/
|
|
78
|
-
export function resolveOwnerConfigPath() {
|
|
79
|
-
return join(resolvePrivateConfigDir(), 'owner.yaml');
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// ---------------------------------------------------------------------------
|
|
83
|
-
// Loader
|
|
84
|
-
// ---------------------------------------------------------------------------
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Load and validate the owner.yaml config from disk.
|
|
88
|
-
*
|
|
89
|
-
* Defensive — never throws. Distinguishes four outcomes via the `source`
|
|
90
|
-
* field so callers can react differently to "missing file" (opt-in feature
|
|
91
|
-
* not yet enabled by the user) vs "malformed YAML" (broken state, surface
|
|
92
|
-
* to user).
|
|
93
|
-
*
|
|
94
|
-
* @param {{path?: string}} [opts]
|
|
95
|
-
* @returns {Promise<{ok: boolean, value: object|null, errors: string[], source: string, path: string}>}
|
|
96
|
-
*/
|
|
97
|
-
export async function loadOwnerConfig(opts = {}) {
|
|
98
|
-
const path = opts.path ?? resolveOwnerConfigPath();
|
|
99
|
-
|
|
100
|
-
if (!existsSync(path)) {
|
|
101
|
-
return {
|
|
102
|
-
ok: false,
|
|
103
|
-
value: null,
|
|
104
|
-
errors: [],
|
|
105
|
-
source: 'missing',
|
|
106
|
-
path,
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
let raw;
|
|
111
|
-
try {
|
|
112
|
-
raw = await readFile(path, 'utf8');
|
|
113
|
-
} catch (err) {
|
|
114
|
-
return {
|
|
115
|
-
ok: false,
|
|
116
|
-
value: null,
|
|
117
|
-
errors: [`failed to read owner config: ${err.message}`],
|
|
118
|
-
source: 'parse-error',
|
|
119
|
-
path,
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
let parsed;
|
|
124
|
-
try {
|
|
125
|
-
// js-yaml's default schema (CORE_SCHEMA) is safe — it explicitly excludes
|
|
126
|
-
// the !!js/function and !!js/regexp tags from the legacy DEFAULT_SCHEMA
|
|
127
|
-
// that would let a malicious YAML file execute code or pollute prototypes.
|
|
128
|
-
parsed = yaml.load(raw);
|
|
129
|
-
} catch (err) {
|
|
130
|
-
return {
|
|
131
|
-
ok: false,
|
|
132
|
-
value: null,
|
|
133
|
-
errors: [`YAML parse error: ${err.message}`],
|
|
134
|
-
source: 'parse-error',
|
|
135
|
-
path,
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// Empty file or YAML "null" → treat as parse-error so the caller knows
|
|
140
|
-
// there's nothing to merge (vs a missing file which opts the user out
|
|
141
|
-
// entirely).
|
|
142
|
-
if (parsed === null || parsed === undefined) {
|
|
143
|
-
return {
|
|
144
|
-
ok: false,
|
|
145
|
-
value: null,
|
|
146
|
-
errors: ['owner config is empty'],
|
|
147
|
-
source: 'parse-error',
|
|
148
|
-
path,
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
const validation = validate(parsed);
|
|
153
|
-
if (!validation.ok) {
|
|
154
|
-
return {
|
|
155
|
-
ok: false,
|
|
156
|
-
value: null,
|
|
157
|
-
errors: validation.errors,
|
|
158
|
-
source: 'validation-error',
|
|
159
|
-
path,
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
return {
|
|
164
|
-
ok: true,
|
|
165
|
-
value: validation.value,
|
|
166
|
-
errors: [],
|
|
167
|
-
source: 'file',
|
|
168
|
-
path,
|
|
169
|
-
};
|
|
170
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* owner-config.mjs — Owner Persona schema + validator (Issue #174, Sub-Epic #161).
|
|
3
|
-
*
|
|
4
|
-
* Thin re-export wrapper. All logic lives in `./owner-config/` submodules:
|
|
5
|
-
* constants.mjs — frozen enum arrays + schema version
|
|
6
|
-
* error.mjs — OwnerConfigError class
|
|
7
|
-
* defaults.mjs — defaults() factory
|
|
8
|
-
* validate.mjs — validate() + 7 section helpers
|
|
9
|
-
* coerce.mjs — coerce() strict-mode wrapper
|
|
10
|
-
* merge.mjs — merge() deep-merge
|
|
11
|
-
* index.mjs — re-export hub (aggregates all 10 public symbols)
|
|
12
|
-
*
|
|
13
|
-
* For the full schema contract and module DAG see `./owner-config/index.mjs`.
|
|
14
|
-
* The loader (which does touch the disk) lives in `owner-config-loader.mjs`.
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
export {
|
|
18
|
-
CURRENT_OWNER_SCHEMA_VERSION,
|
|
19
|
-
VALID_TONE_STYLES,
|
|
20
|
-
VALID_OUTPUT_LEVELS,
|
|
21
|
-
VALID_PREAMBLE_LEVELS,
|
|
22
|
-
VALID_COMMENTS_LEVELS,
|
|
23
|
-
OwnerConfigError,
|
|
24
|
-
defaults,
|
|
25
|
-
validate,
|
|
26
|
-
coerce,
|
|
27
|
-
merge,
|
|
28
|
-
} from './owner-config/index.mjs';
|
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* soul-resolve.mjs — Template resolver for soul.md files (Issue #176, D3).
|
|
3
|
-
*
|
|
4
|
-
* Resolves `{{slot}}` placeholders in soul.md templates using owner persona
|
|
5
|
-
* config loaded via `owner-yaml.mjs` (D1). Pure at the `resolveSoul` level;
|
|
6
|
-
* `loadAndResolveSoul` performs disk I/O.
|
|
7
|
-
*
|
|
8
|
-
* ── NO RUNTIME CALLER — read this before assuming a soul.md is resolved ──────
|
|
9
|
-
*
|
|
10
|
-
* Nothing in `scripts/`, `hooks/`, or any skill body calls either export. Skill
|
|
11
|
-
* bodies instruct the coordinator to read soul.md DIRECTLY, so whatever is in
|
|
12
|
-
* the file on disk is what the coordinator sees — an unsubstituted `{{slot}}`
|
|
13
|
-
* reaches the model verbatim and instructs nothing. `skills/session-start/soul.md`
|
|
14
|
-
* is therefore authored pre-resolved: it carries no slots, and the operator's
|
|
15
|
-
* `efficiency.output-level` selects one of its literal `### output-level: <value>`
|
|
16
|
-
* blocks (see that file's § Output Levels; the skill body performs the lookup).
|
|
17
|
-
* Slots that remain in other soul.md files are inert for the same reason.
|
|
18
|
-
* Do not add a slot to a soul.md expecting substitution — wire a caller first.
|
|
19
|
-
*
|
|
20
|
-
* ── Slot syntax ──────────────────────────────────────────────────────────────
|
|
21
|
-
*
|
|
22
|
-
* {{owner.language}} → 'de' | 'en'
|
|
23
|
-
* {{tone.style}} → 'direct' | 'neutral' | 'friendly'
|
|
24
|
-
* {{efficiency.output-level}} → 'lite' | 'full' | 'ultra'
|
|
25
|
-
* {{efficiency.preamble}} → 'minimal' | 'verbose'
|
|
26
|
-
*
|
|
27
|
-
* ── Resolution rules ─────────────────────────────────────────────────────────
|
|
28
|
-
*
|
|
29
|
-
* - Known slot path present in ownerConfig → replaced with the value
|
|
30
|
-
* - Known slot path missing in ownerConfig → replaced with default (silent)
|
|
31
|
-
* - Unknown slot path → left as-is; warning added to result
|
|
32
|
-
*
|
|
33
|
-
* ── Exports ───────────────────────────────────────────────────────────────────
|
|
34
|
-
*
|
|
35
|
-
* resolveSoul(templateContent, ownerConfig) → { resolved: string, warnings: string[] }
|
|
36
|
-
* loadAndResolveSoul(soulPath, opts?) → { resolved: string, warnings: string[], source: string }
|
|
37
|
-
*/
|
|
38
|
-
|
|
39
|
-
import { readFileSync } from 'node:fs';
|
|
40
|
-
import { loadOwnerConfig, getDefaults } from './owner-yaml.mjs';
|
|
41
|
-
|
|
42
|
-
// ---------------------------------------------------------------------------
|
|
43
|
-
// Helpers
|
|
44
|
-
// ---------------------------------------------------------------------------
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Look up a dot-notation path such as "efficiency.output-level" in a nested
|
|
48
|
-
* object. Returns `undefined` if any segment is missing.
|
|
49
|
-
*
|
|
50
|
-
* @param {object} obj
|
|
51
|
-
* @param {string} dotPath
|
|
52
|
-
* @returns {unknown}
|
|
53
|
-
*/
|
|
54
|
-
function getByPath(obj, dotPath) {
|
|
55
|
-
const segments = dotPath.split('.');
|
|
56
|
-
let cursor = obj;
|
|
57
|
-
for (const seg of segments) {
|
|
58
|
-
if (cursor === null || cursor === undefined || typeof cursor !== 'object') {
|
|
59
|
-
return undefined;
|
|
60
|
-
}
|
|
61
|
-
cursor = cursor[seg];
|
|
62
|
-
}
|
|
63
|
-
return cursor;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Set of slot paths explicitly documented / known by this module.
|
|
68
|
-
* Any slot NOT in this set is considered unknown and left in place with a warning.
|
|
69
|
-
*/
|
|
70
|
-
const KNOWN_SLOTS = new Set([
|
|
71
|
-
'owner.language',
|
|
72
|
-
'tone.style',
|
|
73
|
-
'efficiency.output-level',
|
|
74
|
-
'efficiency.preamble',
|
|
75
|
-
]);
|
|
76
|
-
|
|
77
|
-
// ---------------------------------------------------------------------------
|
|
78
|
-
// Public API
|
|
79
|
-
// ---------------------------------------------------------------------------
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Resolve `{{slot}}` placeholders in `templateContent` using `ownerConfig`.
|
|
83
|
-
*
|
|
84
|
-
* Pure function — no I/O, no side-effects.
|
|
85
|
-
*
|
|
86
|
-
* @param {string} templateContent Raw soul.md template text.
|
|
87
|
-
* @param {object} ownerConfig Owner persona config (from loadOwnerConfig or getDefaults).
|
|
88
|
-
* @returns {{ resolved: string, warnings: string[] }}
|
|
89
|
-
*/
|
|
90
|
-
export function resolveSoul(templateContent, ownerConfig) {
|
|
91
|
-
const defaults = getDefaults();
|
|
92
|
-
const warnings = [];
|
|
93
|
-
|
|
94
|
-
const resolved = templateContent.replace(/\{\{\s*([\w.-]+)\s*\}\}/g, (_match, path) => {
|
|
95
|
-
if (!KNOWN_SLOTS.has(path)) {
|
|
96
|
-
warnings.push(`Unknown slot path: {{${path}}} — left in place`);
|
|
97
|
-
return `{{${path}}}`;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// Try ownerConfig first, then fall back to defaults
|
|
101
|
-
let value = getByPath(ownerConfig, path);
|
|
102
|
-
if (value === undefined || value === null || value === '') {
|
|
103
|
-
value = getByPath(defaults, path);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
return String(value ?? '');
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
return { resolved, warnings };
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Read a soul.md template from `soulPath`, load owner config (from
|
|
114
|
-
* `ownerConfigPath` or the default location), and return the resolved content.
|
|
115
|
-
*
|
|
116
|
-
* @param {string} soulPath Absolute path to the soul.md template.
|
|
117
|
-
* @param {{ ownerConfigPath?: string }} [opts]
|
|
118
|
-
* @returns {{ resolved: string, warnings: string[], source: 'file'|'defaults'|'partial' }}
|
|
119
|
-
*/
|
|
120
|
-
export function loadAndResolveSoul(soulPath, opts = {}) {
|
|
121
|
-
const templateContent = readFileSync(soulPath, 'utf8');
|
|
122
|
-
|
|
123
|
-
const { config, source } = loadOwnerConfig(
|
|
124
|
-
opts.ownerConfigPath ? { path: opts.ownerConfigPath } : {},
|
|
125
|
-
);
|
|
126
|
-
|
|
127
|
-
const { resolved, warnings } = resolveSoul(templateContent, config);
|
|
128
|
-
|
|
129
|
-
return { resolved, warnings, source };
|
|
130
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* render.mjs — Re-export barrel for vault-mirror render modules (Issue #283 split).
|
|
3
|
-
*
|
|
4
|
-
* Consumers can import from this file or directly from render-learnings.mjs / render-sessions.mjs.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
export { detectLearningSchema, generateLearningNote, generateLearningNoteV2 } from './render-learnings.mjs';
|
|
8
|
-
export { detectSessionSchema, generateSessionNote, generateSessionNoteV2, generateSessionNoteV3 } from './render-sessions.mjs';
|