simt-dev 0.7.1 → 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 +36 -1
- package/README.md +169 -80
- package/docs/decisions/ADR-014-workspace-is-configuration.md +1 -1
- package/docs/decisions/ADR-025-a-baseline-is-a-region-in-a-file-the-repo-owns.md +1 -1
- package/docs/decisions/ADR-026-a-config-without-a-region-is-a-file-the-repo-owns.md +1 -1
- package/docs/decisions/ADR-027-the-one-gitattributes-line-that-is-neither-dangerous-nor-empty.md +1 -1
- package/docs/decisions/ADR-028-a-base-file-is-a-floor-so-the-disagreement-it-cannot-repair-becomes-a-check.md +1 -1
- package/docs/decisions/ADR-029-the-pins-a-repository-declares-are-its-own.md +1 -1
- package/docs/decisions/ADR-030-what-a-plan-does-not-ship-is-a-decision-with-a-record.md +1 -1
- package/docs/decisions/ADR-048-a-baseline-carries-only-keys-a-reader-honours.md +1 -1
- package/docs/decisions/ADR-082-the-tracker-override-is-a-manifest-key.md +1 -1
- package/docs/decisions/ADR-126-the-mcp-surface-stays-a-reader-so-there-is-no-write-path-to-build.md +68 -0
- package/docs/decisions/ADR-127-the-mcp-tool-list-is-written-by-hand-and-roots-is-not-asked-for.md +65 -0
- package/docs/decisions/ADR-128-a-research-plan-is-a-plan-and-the-tracker-reads-it.md +42 -0
- package/ecosystems/bun.json +6 -0
- package/ecosystems/cargo.json +25 -0
- package/ecosystems/composer.json +6 -0
- package/ecosystems/dart.json +24 -0
- package/ecosystems/deno.json +21 -0
- package/ecosystems/devcontainers.json +6 -0
- package/ecosystems/docker-compose.json +6 -0
- package/ecosystems/docker.json +6 -0
- package/ecosystems/dotnet.json +25 -0
- package/ecosystems/elixir.json +23 -0
- package/ecosystems/gitsubmodule.json +6 -0
- package/ecosystems/go.json +25 -0
- package/ecosystems/gradle.json +13 -0
- package/ecosystems/maven.json +23 -0
- package/ecosystems/node.json +25 -0
- package/ecosystems/python.json +20 -0
- package/ecosystems/ruby.json +23 -0
- package/ecosystems/swift.json +25 -0
- package/ecosystems/terraform.json +6 -0
- package/ecosystems/uv.json +6 -0
- package/harnesses/antigravity.json +162 -0
- package/harnesses/claude.json +206 -0
- package/harnesses/codex.json +136 -0
- package/harnesses/copilot.json +88 -0
- package/harnesses/cursor.json +140 -0
- package/harnesses/deepseek.json +99 -0
- package/harnesses/gemini.json +122 -0
- package/harnesses/opencode.json +173 -0
- package/package.json +3 -1
- package/rules/mdc/intake-questions.mdc +8 -0
- package/src/advice/advice.mjs +3 -2
- package/src/agenthooks/render.mjs +86 -180
- package/src/agents/render.mjs +15 -18
- package/src/api.mjs +7 -5
- package/src/argv.mjs +19 -0
- package/src/bench/ablate.mjs +3 -4
- package/src/bench/adapters.json +155 -113
- package/src/bench/adapters.mjs +466 -0
- package/src/bench/arms.mjs +333 -0
- package/src/bench/grade.mjs +12 -26
- package/src/bench/graders.mjs +42 -0
- package/src/bench/mine.mjs +17 -535
- package/src/bench/probe-ledger.mjs +149 -0
- package/src/bench/probes.catalog.mjs +918 -0
- package/src/bench/probes.mjs +15 -1037
- package/src/bench/render.mjs +111 -0
- package/src/bench/report.mjs +116 -971
- package/src/bench/run.mjs +164 -881
- package/src/bench/variants.mjs +4 -5
- package/src/bench/workspace.mjs +160 -0
- package/src/brief/assemble.mjs +11 -13
- package/src/ci/dependabot.mjs +25 -55
- package/src/commands/agent.mjs +1 -9
- package/src/commands/agenthook.mjs +66 -35
- package/src/commands/bench.mjs +2 -2
- package/src/commands/ci.mjs +11 -13
- package/src/commands/docs.mjs +88 -0
- package/src/commands/gate.mjs +10 -13
- package/src/commands/harness.mjs +3 -3
- package/src/commands/help.mjs +44 -3
- package/src/commands/hooks.mjs +20 -20
- package/src/commands/ignore.mjs +11 -7
- package/src/commands/impact.mjs +4 -8
- package/src/commands/index.mjs +49 -9
- package/src/commands/init-scan.mjs +90 -0
- package/src/commands/init.mjs +122 -104
- package/src/commands/lease.mjs +67 -18
- package/src/commands/map.mjs +10 -5
- package/src/commands/mcp.mjs +2 -17
- package/src/commands/repoindex.mjs +8 -8
- package/src/commands/rules.mjs +8 -4
- package/src/commands/shared.mjs +12 -16
- package/src/commands/skills.mjs +5 -6
- package/src/commands/skillscompile.mjs +3 -3
- package/src/commands/skillsinstall.mjs +5 -4
- package/src/commands/slice.mjs +199 -64
- package/src/commands/spec.mjs +78 -62
- package/src/commands/status.mjs +24 -12
- package/src/commands/sync.mjs +14 -14
- package/src/commands/tests.mjs +12 -12
- package/src/commands/tokens.mjs +19 -24
- package/src/commands/upgrade.mjs +8 -4
- package/src/commands/why.mjs +6 -13
- package/src/compare/hooksstate.mjs +2 -6
- package/src/compare/learn.mjs +3 -1
- package/src/decisions/index.mjs +3 -2
- package/src/docs/commands.mjs +65 -0
- package/src/ecosystem/records.mjs +219 -0
- package/src/explain.mjs +31 -0
- package/src/gates/index.mjs +385 -0
- package/src/graph/compile.mjs +8 -4
- package/src/graph/load.mjs +2 -1
- package/src/graph/spec.mjs +10 -8
- package/src/harness/records.mjs +143 -0
- package/src/hooks/capability.mjs +5 -3
- package/src/hooks/dispatcher.mjs +26 -32
- package/src/hooks/gates.mjs +12 -2
- package/src/hooks/resolve.mjs +2 -7
- package/src/hooks/where.mjs +2 -7
- package/src/identity.mjs +2 -11
- package/src/index/coverage.mjs +58 -0
- package/src/index/extract.mjs +6 -16
- package/src/index/gate.mjs +1 -1
- package/src/index/govern.mjs +26 -9
- package/src/index/impact.mjs +4 -8
- package/src/index/map.mjs +14 -15
- package/src/index/open.mjs +173 -0
- package/src/index/persist.mjs +224 -57
- package/src/index/store.mjs +82 -39
- package/src/index/tests.mjs +2 -6
- package/src/journal.mjs +16 -11
- package/src/kernel/card.mjs +37 -63
- package/src/kernel/checks.mjs +4 -10
- package/src/lease.mjs +102 -7
- package/src/ledger/index.mjs +124 -17
- package/src/mcp/paginate.mjs +33 -0
- package/src/mcp/schemas.mjs +512 -0
- package/src/mcp/server.mjs +49 -9
- package/src/mcp/tools-facts.mjs +326 -0
- package/src/mcp/tools-read.mjs +221 -0
- package/src/mcp/tools.mjs +4 -1005
- package/src/{bench → measure}/corpus.mjs +1 -1
- package/src/measure/metrics.mjs +236 -0
- package/src/measure/records.mjs +211 -0
- package/src/measure/stats.mjs +538 -0
- package/src/measure/task-schema.mjs +125 -0
- package/src/measure/tasks.mjs +69 -0
- package/src/memory/decide.mjs +6 -15
- package/src/memory/lesson-text.mjs +61 -0
- package/src/memory/lessons.mjs +29 -82
- package/src/name.mjs +7 -0
- package/src/output/index.mjs +122 -31
- package/src/packs/manage.mjs +15 -9
- package/src/plan/index.mjs +1 -1
- package/src/plugins.mjs +22 -16
- package/src/prbody.mjs +7 -11
- package/src/projection/loaders.mjs +7 -692
- package/src/projection/manifest.mjs +10 -435
- package/src/projection/mdc.mjs +7 -8
- package/src/projection/project.mjs +126 -221
- package/src/projection/region.mjs +561 -0
- package/src/projection/rules.mjs +16 -547
- package/src/projection/surfaces.mjs +43 -210
- package/src/projection/targets.mjs +5 -5
- package/src/repo-root.mjs +13 -1
- package/src/resolve/eval.mjs +2 -1
- package/src/resolve/fingerprint.mjs +0 -7
- package/src/resolve/resolve.mjs +1 -41
- package/src/review/critic.mjs +8 -12
- package/src/run.mjs +98 -0
- package/src/secrets.mjs +14 -22
- package/src/state/events.mjs +129 -0
- package/src/status-snapshot.mjs +233 -0
- package/src/status.mjs +2 -6
- package/src/sys/git.mjs +82 -0
- package/src/{bench/process.mjs → sys/proc.mjs} +28 -5
- package/src/testing/parsers.mjs +265 -0
- package/src/testing/patches.mjs +23 -0
- package/src/testing/paths.mjs +49 -0
- package/src/testing/runners.mjs +186 -0
- package/src/testing/vault.mjs +68 -0
- package/src/text/frontmatter.mjs +49 -0
- package/src/text/tokens.mjs +52 -0
- package/src/toolwords.mjs +1 -1
- package/src/util/fs.mjs +20 -0
- package/src/util/sort.mjs +12 -0
- package/src/workspace.mjs +3 -1
- package/src/write/plan.mjs +82 -0
- package/templates/plan/tracker.json +1 -0
- package/docs/decisions/ADR-033-three-plan-rows-closed-by-decision-not-by-authoring.md +0 -82
- package/docs/decisions/ADR-102-the-two-queued-review-rows-are-closed-by-ruling.md +0 -49
- package/src/bench/tasks.mjs +0 -36
package/src/commands/mcp.mjs
CHANGED
|
@@ -9,17 +9,14 @@
|
|
|
9
9
|
// misconfigured the command needs to be able to ask what it exposes without hanging, and
|
|
10
10
|
// a command that only speaks when spoken to cannot answer that.
|
|
11
11
|
|
|
12
|
-
import { readFile } from 'node:fs/promises';
|
|
13
|
-
import { fileURLToPath } from 'node:url';
|
|
14
|
-
import { dirname, join } from 'node:path';
|
|
15
|
-
|
|
16
12
|
import { EXIT, emit, emitJson, fail, isJson } from '../output/index.mjs';
|
|
17
13
|
import { TOOLS } from '../mcp/tools.mjs';
|
|
18
14
|
import { PROTOCOL_VERSION, SUPPORTED_VERSIONS, serve } from '../mcp/server.mjs';
|
|
19
15
|
import { CLI } from '../name.mjs';
|
|
16
|
+
import { packageVersion } from '../package-version.mjs';
|
|
20
17
|
|
|
21
18
|
export async function mcpCommand(positional, flags) {
|
|
22
|
-
const
|
|
19
|
+
const version = packageVersion();
|
|
23
20
|
|
|
24
21
|
if (flags.list === true) {
|
|
25
22
|
if (isJson(flags)) {
|
|
@@ -62,16 +59,4 @@ export async function mcpCommand(positional, flags) {
|
|
|
62
59
|
} catch (error) {
|
|
63
60
|
return fail(`mcp transport failed: ${error.message}`, EXIT.FAIL, `run: ${CLI} mcp --list to check the surface loads`);
|
|
64
61
|
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
async function packageVersion() {
|
|
68
|
-
try {
|
|
69
|
-
const here = dirname(fileURLToPath(import.meta.url));
|
|
70
|
-
const pkg = JSON.parse(await readFile(join(here, '..', '..', 'package.json'), 'utf8'));
|
|
71
|
-
return { version: pkg.version };
|
|
72
|
-
} catch {
|
|
73
|
-
// A missing version must not stop the server: the client already knows which
|
|
74
|
-
// build it launched, and a hard failure here would look like a protocol bug.
|
|
75
|
-
return { version: 'unknown' };
|
|
76
|
-
}
|
|
77
62
|
}
|
|
@@ -2,12 +2,11 @@
|
|
|
2
2
|
// content: it is gitignored, and a second run with the same blobs writes the same
|
|
3
3
|
// rows (ADR-089).
|
|
4
4
|
|
|
5
|
-
import { execFile } from 'node:child_process';
|
|
6
|
-
|
|
7
5
|
import { EXIT, emit, emitJson, fail, isJson } from '../output/index.mjs';
|
|
8
6
|
import { CLI } from '../name.mjs';
|
|
9
7
|
import { JSONL_FILE, SQLITE_FILE, reindex } from '../index/store.mjs';
|
|
10
8
|
import { commandRoot } from '../repo-root.mjs';
|
|
9
|
+
import { git } from '../sys/git.mjs';
|
|
11
10
|
|
|
12
11
|
export async function indexCommand(args, flags) {
|
|
13
12
|
if (args.length > 0) return fail(`unknown index argument ${args[0]}`, EXIT.USAGE, `run: ${CLI} index`);
|
|
@@ -51,10 +50,11 @@ export async function indexCommand(args, flags) {
|
|
|
51
50
|
return EXIT.OK;
|
|
52
51
|
}
|
|
53
52
|
|
|
54
|
-
function isIgnored(root, file) {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
53
|
+
async function isIgnored(root, file) {
|
|
54
|
+
try {
|
|
55
|
+
await git(root, ['check-ignore', '-q', '--', file]);
|
|
56
|
+
return true;
|
|
57
|
+
} catch {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
60
|
}
|
package/src/commands/rules.mjs
CHANGED
|
@@ -18,7 +18,8 @@ import { readdir } from 'node:fs/promises';
|
|
|
18
18
|
import { repoContext } from '../context.mjs';
|
|
19
19
|
import { join } from 'node:path';
|
|
20
20
|
import { renderDecisionsIndex } from '../decisions/index-doc.mjs';
|
|
21
|
-
import {
|
|
21
|
+
import { applyPlan, change } from '../write/plan.mjs';
|
|
22
|
+
import { readBefore } from '../diff.mjs';
|
|
22
23
|
import { EXIT, emit, emitJson, fail, isJson } from '../output/index.mjs';
|
|
23
24
|
import { loadRules, select } from '../projection/project.mjs';
|
|
24
25
|
import { loadMdcRules } from '../projection/mdc.mjs';
|
|
@@ -529,9 +530,12 @@ export async function decisionsCommand(args, flags) {
|
|
|
529
530
|
// table - the index is not internal state even though it is generated.
|
|
530
531
|
if (flags.yes === true) {
|
|
531
532
|
const rootDir = root ?? process.cwd();
|
|
532
|
-
const
|
|
533
|
-
await
|
|
534
|
-
|
|
533
|
+
const index = join(rootDir, 'docs', 'decisions-index.md');
|
|
534
|
+
await applyPlan(
|
|
535
|
+
rootDir,
|
|
536
|
+
[change({ path: index, before: await readBefore(index), after: renderDecisionsIndex(rows), owner: 'rules decisions', reason: 'the index follows the decision files' })],
|
|
537
|
+
{ mode: { apply: true }, label: 'rules decisions' },
|
|
538
|
+
);
|
|
535
539
|
emit(`wrote docs/decisions-index.md (${rows.length} decisions)`);
|
|
536
540
|
return EXIT.OK;
|
|
537
541
|
}
|
package/src/commands/shared.mjs
CHANGED
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
} from '../projection/shared.mjs';
|
|
30
30
|
import { buildManifest, declaredShared, manifestPath, manifestText, readManifest, writeManifest } from '../projection/manifest.mjs';
|
|
31
31
|
import { desiredRegion } from '../projection/rules.mjs';
|
|
32
|
+
import { plainNumber } from '../util/fs.mjs';
|
|
32
33
|
|
|
33
34
|
/**
|
|
34
35
|
* Everything `plan` and `install` both need, computed once.
|
|
@@ -76,9 +77,9 @@ async function gather(flags) {
|
|
|
76
77
|
*/
|
|
77
78
|
function divergentNote(d, total) {
|
|
78
79
|
if (d.reason === 'coverage') {
|
|
79
|
-
return `${d.id} carried by ${
|
|
80
|
+
return `${d.id} carried by ${plainNumber(d.roots.length)} of ${plainNumber(total)} repositories, so each region keeps its own`;
|
|
80
81
|
}
|
|
81
|
-
return `${d.id} rendered differently in ${
|
|
82
|
+
return `${d.id} rendered differently in ${plainNumber(d.roots.length)} repositories, so each region keeps its own`;
|
|
82
83
|
}
|
|
83
84
|
|
|
84
85
|
/** Why a selected rule cannot leave any region, in the words its reason deserves. */
|
|
@@ -98,7 +99,7 @@ function excludedNote({ id, why }) {
|
|
|
98
99
|
function capNote(target, bytes) {
|
|
99
100
|
if (target.path === null || target.cap === null || bytes <= target.cap.bytes) return null;
|
|
100
101
|
return [
|
|
101
|
-
`${target.surface} ${
|
|
102
|
+
`${target.surface} ${plainNumber(bytes)} B exceeds the ${plainNumber(target.cap.bytes)} B it allows for ${target.cap.appliesTo}`,
|
|
102
103
|
` ${target.cap.why}`,
|
|
103
104
|
` the rules above that limit are not read; move rules this workspace does not share back into each region, or drop the shared copy for this surface`,
|
|
104
105
|
].join('\n');
|
|
@@ -157,10 +158,10 @@ export async function sharedPlanCommand(_args, flags) {
|
|
|
157
158
|
}
|
|
158
159
|
|
|
159
160
|
const lines = [
|
|
160
|
-
`shared ${target.root} ${
|
|
161
|
-
`rules ${
|
|
161
|
+
`shared ${target.root} ${plainNumber(roots.length)} repositories (${target.source})`,
|
|
162
|
+
`rules ${plainNumber(shared.length)} of ${plainNumber(candidates)} ids carried once ${plainNumber(bytes)} B`,
|
|
162
163
|
];
|
|
163
|
-
for (const r of shared) lines.push(` ${r.id} ${r.priority ?? 'unclassified'} ${
|
|
164
|
+
for (const r of shared) lines.push(` ${r.id} ${r.priority ?? 'unclassified'} ${plainNumber(Buffer.byteLength(r.rendered))} B ${r.source}`);
|
|
164
165
|
if (divergent.length > 0) {
|
|
165
166
|
// Named, not summarised: a reader who expected a python rule here and did not get it
|
|
166
167
|
// is owed the reason, and "divergent: 2" is not a reason.
|
|
@@ -171,9 +172,9 @@ export async function sharedPlanCommand(_args, flags) {
|
|
|
171
172
|
lines.push(`left in every region ${excluded.length} rule(s) no root can hand over:`);
|
|
172
173
|
for (const e of excluded) lines.push(` ${excludedNote(e)}`);
|
|
173
174
|
}
|
|
174
|
-
lines.push(`saves ${
|
|
175
|
+
lines.push(`saves ${plainNumber(save)} B across ${plainNumber(roots.length)} regions`);
|
|
175
176
|
for (const r of rows) {
|
|
176
|
-
lines.push(` ${r.name} ${
|
|
177
|
+
lines.push(` ${r.name} ${plainNumber(r.bytes)} B ${r.mode} ${r.rules.length > 0 ? r.rules.join(', ') : '(carries nothing extra)'}`);
|
|
177
178
|
}
|
|
178
179
|
lines.push('writes');
|
|
179
180
|
for (const t of targets) {
|
|
@@ -297,7 +298,7 @@ export async function sharedInstallCommand(_args, flags) {
|
|
|
297
298
|
}
|
|
298
299
|
emit(
|
|
299
300
|
[
|
|
300
|
-
`shared ${
|
|
301
|
+
`shared ${plainNumber(shared.length)} rule(s), ${plainNumber(Buffer.byteLength(sharedBody(shared)))} B`,
|
|
301
302
|
...states.map((s) =>
|
|
302
303
|
s.path === null
|
|
303
304
|
? ` paste ${s.surface} no user-level file is recorded for it`
|
|
@@ -326,8 +327,8 @@ export async function sharedInstallCommand(_args, flags) {
|
|
|
326
327
|
}
|
|
327
328
|
|
|
328
329
|
const lines = [
|
|
329
|
-
`shared installed for ${
|
|
330
|
-
`rules ${
|
|
330
|
+
`shared installed for ${plainNumber(roots.length)} repositories`,
|
|
331
|
+
`rules ${plainNumber(shared.length)} carried once ${shared.map((r) => r.id).join(', ')}`,
|
|
331
332
|
];
|
|
332
333
|
for (const w of written) {
|
|
333
334
|
// `inject`'s word for a file with no region in it is "added"; the word for a file
|
|
@@ -417,11 +418,6 @@ function describeShared(state, targets) {
|
|
|
417
418
|
return `${targets.length} user-level file(s) carry every rule this repository handed over`;
|
|
418
419
|
}
|
|
419
420
|
|
|
420
|
-
/** `1234567`, never `1,234,567`: two machines must print the same bytes (ADR-013). */
|
|
421
|
-
function plain(n) {
|
|
422
|
-
return String(n);
|
|
423
|
-
}
|
|
424
|
-
|
|
425
421
|
/** The directory name, for a line that has to name several repositories at once. */
|
|
426
422
|
function basename(path) {
|
|
427
423
|
return path.split('/').filter(Boolean).pop() ?? path;
|
package/src/commands/skills.mjs
CHANGED
|
@@ -20,12 +20,14 @@ import { bodyFile, sourcePath } from '../resolve/body.mjs';
|
|
|
20
20
|
import { unknownSkills, scoreCorpus } from '../resolve/eval.mjs';
|
|
21
21
|
import { PACKAGE_ROOT, resolveWorkspace, WORKSPACE_REMEDY } from '../workspace.mjs';
|
|
22
22
|
import { fingerprint } from '../resolve/fingerprint.mjs';
|
|
23
|
-
import {
|
|
23
|
+
import { rankSkills, closure, selectWithinBudget, resolveAlias, scoreSkill, selectionVerdict, stackCompatible } from '../resolve/resolve.mjs';
|
|
24
|
+
import { tokenize } from '../text/tokens.mjs';
|
|
24
25
|
import { localSkillNodes } from '../resolve/local.mjs';
|
|
25
26
|
import { nearMatches } from '../resolve/near.mjs';
|
|
26
27
|
import { CLI } from '../name.mjs';
|
|
27
28
|
import { parsePathList } from '../argv.mjs';
|
|
28
29
|
import { commandRoot } from '../repo-root.mjs';
|
|
30
|
+
import { gitText } from '../sys/git.mjs';
|
|
29
31
|
|
|
30
32
|
export { parsePathList };
|
|
31
33
|
|
|
@@ -34,11 +36,8 @@ const DEFAULT_BUDGET = 20480;
|
|
|
34
36
|
/** git status paths, so path signals come from the real diff. */
|
|
35
37
|
export async function dirtyPaths(root) {
|
|
36
38
|
try {
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
const run = promisify(execFile);
|
|
40
|
-
const { stdout } = await run('git', ['-C', root, 'status', '--porcelain=v1'], { maxBuffer: 8 << 20 });
|
|
41
|
-
return stdout
|
|
39
|
+
const stdout = await gitText(root, ['status', '--porcelain=v1'], { maxBuffer: 8 << 20 });
|
|
40
|
+
return (stdout ?? '')
|
|
42
41
|
.split('\n')
|
|
43
42
|
.filter(Boolean)
|
|
44
43
|
.map((line) => line.slice(3).trim())
|
|
@@ -14,9 +14,9 @@ import {
|
|
|
14
14
|
evaluateSkillTriggers,
|
|
15
15
|
proposeSkillDescription,
|
|
16
16
|
} from '../graph/compile.mjs';
|
|
17
|
-
import { loadRunRecords } from '../
|
|
18
|
-
import { extractPatchFiles } from '../
|
|
19
|
-
import { resolveVaultDir } from '../
|
|
17
|
+
import { loadRunRecords } from '../measure/records.mjs';
|
|
18
|
+
import { extractPatchFiles } from '../testing/patches.mjs';
|
|
19
|
+
import { resolveVaultDir } from '../testing/vault.mjs';
|
|
20
20
|
import { locateSource, sourceHeldLocally } from '../resolve/source.mjs';
|
|
21
21
|
import { nearMatches } from '../resolve/near.mjs';
|
|
22
22
|
import { resolveWorkspace, WORKSPACE_REMEDY } from '../workspace.mjs';
|
|
@@ -39,6 +39,7 @@ import { CLI } from '../name.mjs';
|
|
|
39
39
|
import { MANIFEST_FILE } from '../name.mjs';
|
|
40
40
|
import { nameProblems } from '../graph/spec.mjs';
|
|
41
41
|
import { commandRoot } from '../repo-root.mjs';
|
|
42
|
+
import { parseFrontmatter } from '../text/frontmatter.mjs';
|
|
42
43
|
|
|
43
44
|
/**
|
|
44
45
|
* Where skills live in this repo. The estate already has a convention - a
|
|
@@ -498,13 +499,13 @@ async function dirEntries(root) {
|
|
|
498
499
|
*/
|
|
499
500
|
export async function wrapFlat(src, name) {
|
|
500
501
|
const text = await readFile(src, 'utf8');
|
|
501
|
-
const
|
|
502
|
-
if (
|
|
503
|
-
const keys =
|
|
502
|
+
const { front, block } = parseFrontmatter(text);
|
|
503
|
+
if (front === null) return `---\nname: ${name}\n---\n\n${text.trim()}\n`;
|
|
504
|
+
const keys = front
|
|
504
505
|
.split('\n')
|
|
505
506
|
.filter((l) => !/^name:/.test(l))
|
|
506
507
|
.join('\n');
|
|
507
|
-
return `---\nname: ${name}\n${keys}\n---\n\n${text.slice(
|
|
508
|
+
return `---\nname: ${name}\n${keys}\n---\n\n${text.slice(block.length).trim()}\n`;
|
|
508
509
|
}
|
|
509
510
|
|
|
510
511
|
async function fileSha(path) {
|