akm-cli 0.9.0-beta.37 → 0.9.0-beta.39
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 +33 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +22 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +22 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +24 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +22 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +25 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +21 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +21 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +23 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +22 -0
- package/dist/commands/sources/init.js +7 -4
- package/dist/commands/sources/stash-skeleton.js +23 -8
- package/dist/core/standards/resolve-standards-context.js +33 -2
- package/dist/core/standards/resolve-stash-standards.js +12 -0
- package/dist/core/standards/resolve-type-conventions.js +66 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,39 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **Per-type SOFT authoring conventions are now user-editable stash facts.** A
|
|
12
|
+
third authoring-guidance layer joins the hard rules (#645) and general stash
|
|
13
|
+
standards (#642): a stash owner can author
|
|
14
|
+
`facts/conventions/assets/<type>.md` (e.g. `…/skill.md`, `…/command.md`) to
|
|
15
|
+
capture soft, type-specific guidance — voice, structure, length *preference*,
|
|
16
|
+
naming style. When an agent authors a `skill:x`, the body of
|
|
17
|
+
`fact:conventions/assets/skill` is injected (type-scoped — authoring
|
|
18
|
+
`command:y` pulls the `command` convention, never the `skill` one), labeled
|
|
19
|
+
as soft guidance and kept separate from the validator-enforced hard rules.
|
|
20
|
+
The basename must be a `getAssetTypes()`-validated asset type; facts are read
|
|
21
|
+
straight from disk (no index rebuild) and degrade to empty safely. When no
|
|
22
|
+
per-type fact exists, the built-in `TYPE_HINTS` fallback is unchanged (no
|
|
23
|
+
regression). These facts carry soft conventions only and can never weaken the
|
|
24
|
+
authoring contract the gate enforces (`authoringRulesForType` remains the sole
|
|
25
|
+
source of validator-rejecting rules). The general convention/meta resolver now
|
|
26
|
+
excludes `facts/conventions/assets/*` so per-type guidance never leaks
|
|
27
|
+
un-type-scoped into other authoring flows. (#646)
|
|
28
|
+
- **`akm init` now seeds default per-type SOFT convention templates.** Starter
|
|
29
|
+
`facts/conventions/assets/<type>.md` templates ship in the stash skeleton for
|
|
30
|
+
the authored types (`lesson, skill, command, agent, knowledge, memory,
|
|
31
|
+
workflow, script, fact`; `wiki`/`env`/`secret` excluded) so a stash owner has
|
|
32
|
+
an editable starting point. Each expands the matching built-in `TYPE_HINTS`
|
|
33
|
+
one-liner into soft starter guidance, carries `category: convention`
|
|
34
|
+
frontmatter, and states in-body that it is advice, not enforced — it carries
|
|
35
|
+
**no** validator-rejecting rules, so editing or deleting one cannot weaken the
|
|
36
|
+
gate (#645). The stash-skeleton copy is now recursive (preserving nested
|
|
37
|
+
subpaths), and `akm init` seeds **unconditionally** rather than only on first
|
|
38
|
+
create: re-running it on an existing stash backfills any missing skeleton,
|
|
39
|
+
convention, or `.meta/index.md` files. Seeding stays absent-only and never
|
|
40
|
+
overwrites a user-edited file. (#646)
|
|
41
|
+
|
|
9
42
|
## [0.9.0-beta.36] — 2026-06-22
|
|
10
43
|
|
|
11
44
|
### Added
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
category: convention
|
|
3
|
+
description: Starter SOFT authoring conventions for agent assets — edit to taste.
|
|
4
|
+
when_to_use: Surfaced to authoring agents when they write or revise an agent asset.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
<!--
|
|
8
|
+
SOFT guidance only — advice, not a contract. Nothing here is enforced by the
|
|
9
|
+
proposal gate; the validator-rejecting HARD rules live in
|
|
10
|
+
src/core/authoring-rules.ts (#645) and remain the sole enforced source. Editing
|
|
11
|
+
or deleting this file cannot weaken the gate.
|
|
12
|
+
-->
|
|
13
|
+
|
|
14
|
+
# Agent authoring conventions
|
|
15
|
+
|
|
16
|
+
An agent is markdown whose frontmatter describes a reusable role.
|
|
17
|
+
|
|
18
|
+
- Frontmatter typically carries `name`, `description`, and optionally `tools` and
|
|
19
|
+
`model`. Write the `description` so a dispatcher knows exactly when to delegate.
|
|
20
|
+
- The body is the system prompt: establish the role, its scope, and its boundaries.
|
|
21
|
+
- Be explicit about what the agent should and should not do, and what it returns.
|
|
22
|
+
- Prefer a focused single-responsibility persona over a broad do-everything agent.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
category: convention
|
|
3
|
+
description: Starter SOFT authoring conventions for command assets — edit to taste.
|
|
4
|
+
when_to_use: Surfaced to authoring agents when they write or revise a command asset.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
<!--
|
|
8
|
+
SOFT guidance only — advice, not a contract. Nothing here is enforced by the
|
|
9
|
+
proposal gate; the validator-rejecting HARD rules live in
|
|
10
|
+
src/core/authoring-rules.ts (#645) and remain the sole enforced source. Editing
|
|
11
|
+
or deleting this file cannot weaken the gate.
|
|
12
|
+
-->
|
|
13
|
+
|
|
14
|
+
# Command authoring conventions
|
|
15
|
+
|
|
16
|
+
A command is a markdown prompt template the user invokes by name.
|
|
17
|
+
|
|
18
|
+
- Optional frontmatter carries `name` and `description`; the body *is* the prompt.
|
|
19
|
+
- Write the body as the instruction you want the model to follow when invoked.
|
|
20
|
+
- State the task, the expected inputs, and the shape of the desired output up front.
|
|
21
|
+
- Keep it tight and unambiguous — a command is run repeatedly, so vagueness compounds.
|
|
22
|
+
- Use clear placeholders for any arguments the caller will supply.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
category: convention
|
|
3
|
+
description: Starter SOFT authoring conventions for fact assets — edit to taste.
|
|
4
|
+
when_to_use: Surfaced to authoring agents when they write or revise a fact asset.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
<!--
|
|
8
|
+
SOFT guidance only — advice, not a contract. Nothing here is enforced by the
|
|
9
|
+
proposal gate; the validator-rejecting HARD rules live in
|
|
10
|
+
src/core/authoring-rules.ts (#645) and remain the sole enforced source. Editing
|
|
11
|
+
or deleting this file cannot weaken the gate.
|
|
12
|
+
-->
|
|
13
|
+
|
|
14
|
+
# Fact authoring conventions
|
|
15
|
+
|
|
16
|
+
A fact is durable stash-level context — personal, team, or project details,
|
|
17
|
+
coding conventions, or stash-meta.
|
|
18
|
+
|
|
19
|
+
- Frontmatter should include a `description` and a `category`
|
|
20
|
+
(personal | team | project | convention | meta).
|
|
21
|
+
- Set `pinned: true` only for the small always-injected core; most facts stay unpinned.
|
|
22
|
+
- Keep each fact short, high-signal, and self-contained — it is durable context,
|
|
23
|
+
not an episodic note.
|
|
24
|
+
- Write it as a standing declaration that stays true across sessions.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
category: convention
|
|
3
|
+
description: Starter SOFT authoring conventions for knowledge assets — edit to taste.
|
|
4
|
+
when_to_use: Surfaced to authoring agents when they write or revise a knowledge asset.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
<!--
|
|
8
|
+
SOFT guidance only — advice, not a contract. Nothing here is enforced by the
|
|
9
|
+
proposal gate; the validator-rejecting HARD rules live in
|
|
10
|
+
src/core/authoring-rules.ts (#645) and remain the sole enforced source. Editing
|
|
11
|
+
or deleting this file cannot weaken the gate.
|
|
12
|
+
-->
|
|
13
|
+
|
|
14
|
+
# Knowledge authoring conventions
|
|
15
|
+
|
|
16
|
+
A knowledge asset is a reference document meant to be read on demand.
|
|
17
|
+
|
|
18
|
+
- Open with a top-level `# Title` that names the subject plainly.
|
|
19
|
+
- Organise into concise, well-headed sections so a reader can jump to what they need.
|
|
20
|
+
- Favour accuracy and clarity over completeness; link or cross-reference rather than
|
|
21
|
+
duplicating other assets.
|
|
22
|
+
- Voice: explanatory and neutral, written to be re-read months later.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
category: convention
|
|
3
|
+
description: Starter SOFT authoring conventions for lesson assets — edit to taste.
|
|
4
|
+
when_to_use: Surfaced to authoring agents when they write or revise a lesson asset.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
<!--
|
|
8
|
+
SOFT guidance only — advice, not a contract. Nothing here is enforced by the
|
|
9
|
+
proposal gate; the validator-rejecting HARD rules live in
|
|
10
|
+
src/core/authoring-rules.ts (#645) and remain the sole enforced source. Editing
|
|
11
|
+
or deleting this file cannot weaken the gate. Tune the guidance below to match
|
|
12
|
+
how your stash likes lessons written.
|
|
13
|
+
-->
|
|
14
|
+
|
|
15
|
+
# Lesson authoring conventions
|
|
16
|
+
|
|
17
|
+
A lesson captures durable, hard-won judgement about *when* to reach for something
|
|
18
|
+
and *what goes wrong without it* — it is not a restatement of the source asset.
|
|
19
|
+
|
|
20
|
+
- Lead with the trigger: when should a reader reach for this lesson?
|
|
21
|
+
- Then the failure mode: what breaks, or what gets missed, without it?
|
|
22
|
+
- Then the insight: what did real use reveal that the asset itself does not say?
|
|
23
|
+
- Keep it to one to three short, concrete paragraphs. Prefer specifics over
|
|
24
|
+
general advice; a lesson earns its keep by being actionable in a real moment.
|
|
25
|
+
- Voice: direct and practical, written for a future agent mid-task.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
category: convention
|
|
3
|
+
description: Starter SOFT authoring conventions for memory assets — edit to taste.
|
|
4
|
+
when_to_use: Surfaced to authoring agents when they write or revise a memory asset.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
<!--
|
|
8
|
+
SOFT guidance only — advice, not a contract. Nothing here is enforced by the
|
|
9
|
+
proposal gate; the validator-rejecting HARD rules live in
|
|
10
|
+
src/core/authoring-rules.ts (#645) and remain the sole enforced source. Editing
|
|
11
|
+
or deleting this file cannot weaken the gate.
|
|
12
|
+
-->
|
|
13
|
+
|
|
14
|
+
# Memory authoring conventions
|
|
15
|
+
|
|
16
|
+
A memory is a short factual note the user wants persisted across sessions.
|
|
17
|
+
|
|
18
|
+
- Frontmatter usually includes a `description` that states the fact in one line.
|
|
19
|
+
- Keep the body brief and self-contained — one durable fact or decision per memory.
|
|
20
|
+
- Write it so it still reads clearly with no surrounding conversation for context.
|
|
21
|
+
- Prefer durable, reusable facts over episodic play-by-play of a single session.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
category: convention
|
|
3
|
+
description: Starter SOFT authoring conventions for script assets — edit to taste.
|
|
4
|
+
when_to_use: Surfaced to authoring agents when they write or revise a script asset.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
<!--
|
|
8
|
+
SOFT guidance only — advice, not a contract. Nothing here is enforced by the
|
|
9
|
+
proposal gate; the validator-rejecting HARD rules live in
|
|
10
|
+
src/core/authoring-rules.ts (#645) and remain the sole enforced source. Editing
|
|
11
|
+
or deleting this file cannot weaken the gate.
|
|
12
|
+
-->
|
|
13
|
+
|
|
14
|
+
# Script authoring conventions
|
|
15
|
+
|
|
16
|
+
A script is an executable text file stored as-is and run on demand.
|
|
17
|
+
|
|
18
|
+
- Start with a shebang appropriate to the interpreter (e.g. `#!/usr/bin/env bash`).
|
|
19
|
+
- Follow it with a short usage comment: what the script does and how to invoke it.
|
|
20
|
+
- Keep the script focused on one job; fail loudly and early on bad input.
|
|
21
|
+
- Prefer readable, well-commented logic over cleverness — these get re-run by others.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
category: convention
|
|
3
|
+
description: Starter SOFT authoring conventions for skill assets — edit to taste.
|
|
4
|
+
when_to_use: Surfaced to authoring agents when they write or revise a skill asset.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
<!--
|
|
8
|
+
SOFT guidance only — advice, not a contract. Nothing here is enforced by the
|
|
9
|
+
proposal gate; the validator-rejecting HARD rules live in
|
|
10
|
+
src/core/authoring-rules.ts (#645) and remain the sole enforced source. Editing
|
|
11
|
+
or deleting this file cannot weaken the gate.
|
|
12
|
+
-->
|
|
13
|
+
|
|
14
|
+
# Skill authoring conventions
|
|
15
|
+
|
|
16
|
+
A skill is a reusable, self-contained capability stored as `skills/<name>/SKILL.md`.
|
|
17
|
+
|
|
18
|
+
- Frontmatter usually carries `name`, `description`, and `when_to_use`. Write the
|
|
19
|
+
`description` so a dispatcher can decide *from it alone* whether to load the skill.
|
|
20
|
+
- Open the body with the goal in an imperative voice ("Generate…", "Review…").
|
|
21
|
+
- Structure as: purpose, when to use, then the procedure or guidance as ordered
|
|
22
|
+
steps or short sections. Favour scannable headings over long prose.
|
|
23
|
+
- Keep it focused on one capability; split unrelated concerns into separate skills.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
category: convention
|
|
3
|
+
description: Starter SOFT authoring conventions for workflow assets — edit to taste.
|
|
4
|
+
when_to_use: Surfaced to authoring agents when they write or revise a workflow asset.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
<!--
|
|
8
|
+
SOFT guidance only — advice, not a contract. Nothing here is enforced by the
|
|
9
|
+
proposal gate; the validator-rejecting HARD rules live in
|
|
10
|
+
src/core/authoring-rules.ts (#645) and remain the sole enforced source. Editing
|
|
11
|
+
or deleting this file cannot weaken the gate.
|
|
12
|
+
-->
|
|
13
|
+
|
|
14
|
+
# Workflow authoring conventions
|
|
15
|
+
|
|
16
|
+
A workflow describes a multi-step process an agent or human follows in order.
|
|
17
|
+
|
|
18
|
+
- Open with a top-level `# <Title>` naming the workflow's outcome.
|
|
19
|
+
- Lay out the steps as ordered `## Step N` sections, each with a clear action.
|
|
20
|
+
- For each step, state what to do, what it depends on, and how to know it is done.
|
|
21
|
+
- Keep steps atomic and resumable — a reader should be able to stop and pick up midway.
|
|
22
|
+
- Note any branch points or preconditions explicitly rather than burying them in prose.
|
|
@@ -75,10 +75,13 @@ export async function akmInit(options) {
|
|
|
75
75
|
}
|
|
76
76
|
// Ensure the default stash is a local git repo (no remote required)
|
|
77
77
|
ensureGitRepo(stashDir);
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
78
|
+
// Run seeding UNCONDITIONALLY (not just when the stash was newly created) so
|
|
79
|
+
// re-running `akm init` on an existing stash backfills any missing skeleton
|
|
80
|
+
// files — the README, the per-type SOFT convention templates under
|
|
81
|
+
// facts/conventions/assets/, and the `.meta/index.md` orientation doc. Both
|
|
82
|
+
// helpers are absent-only: they never overwrite a file a user has edited.
|
|
83
|
+
copyStashSkeleton(stashDir);
|
|
84
|
+
scaffoldStashMeta(stashDir);
|
|
82
85
|
// Persist stashDir in config.json
|
|
83
86
|
const configPath = getConfigPath();
|
|
84
87
|
const existing = loadUserConfig();
|
|
@@ -6,27 +6,42 @@ import path from "node:path";
|
|
|
6
6
|
import { getDirname } from "../../runtime.js";
|
|
7
7
|
const SKELETON_DIR = path.join(getDirname(import.meta.url), "../../assets/stash-skeleton");
|
|
8
8
|
/**
|
|
9
|
-
* Copy the default stash skeleton into a
|
|
9
|
+
* Copy the default stash skeleton into a stash directory.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
11
|
+
* The skeleton tree under src/assets/stash-skeleton/ is mirrored **recursively**
|
|
12
|
+
* into the stash root, preserving relative subpaths (e.g.
|
|
13
|
+
* `facts/conventions/assets/skill.md` lands at the matching stash subpath).
|
|
14
|
+
* Each file is written only if the destination does not already exist — existing
|
|
15
|
+
* (possibly user-edited) files are never overwritten. Intermediate directories
|
|
16
|
+
* are created as needed.
|
|
17
|
+
*
|
|
18
|
+
* Idempotent and absent-only: running it again on an existing stash backfills
|
|
19
|
+
* any skeleton files that are missing without clobbering present ones. Non-fatal:
|
|
20
|
+
* if the skeleton directory is missing or a copy fails the caller continues.
|
|
15
21
|
*/
|
|
16
22
|
export function copyStashSkeleton(stashDir) {
|
|
23
|
+
copySkeletonDir(SKELETON_DIR, stashDir);
|
|
24
|
+
}
|
|
25
|
+
/** Recursively mirror `srcDir` into `destDir`, writing files only when absent. */
|
|
26
|
+
function copySkeletonDir(srcDir, destDir) {
|
|
17
27
|
let entries;
|
|
18
28
|
try {
|
|
19
|
-
entries = fs.readdirSync(
|
|
29
|
+
entries = fs.readdirSync(srcDir, { withFileTypes: true });
|
|
20
30
|
}
|
|
21
31
|
catch {
|
|
22
32
|
return;
|
|
23
33
|
}
|
|
24
34
|
for (const entry of entries) {
|
|
25
|
-
const src = path.join(
|
|
26
|
-
const dest = path.join(
|
|
35
|
+
const src = path.join(srcDir, entry.name);
|
|
36
|
+
const dest = path.join(destDir, entry.name);
|
|
37
|
+
if (entry.isDirectory()) {
|
|
38
|
+
copySkeletonDir(src, dest);
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
27
41
|
if (fs.existsSync(dest))
|
|
28
42
|
continue;
|
|
29
43
|
try {
|
|
44
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
30
45
|
fs.copyFileSync(src, dest);
|
|
31
46
|
}
|
|
32
47
|
catch {
|
|
@@ -17,8 +17,24 @@
|
|
|
17
17
|
*/
|
|
18
18
|
import { extractWikiNameFromRef, INDEX_MD, LOG_MD, loadWikiSchema, SCHEMA_MD } from "../../wiki/wiki.js";
|
|
19
19
|
import { resolveStashStandards } from "./resolve-stash-standards.js";
|
|
20
|
+
import { resolveTypeConventions, typeConventionRef } from "./resolve-type-conventions.js";
|
|
20
21
|
/** Wiki infra files that are not authored pages (relative to the wiki root). */
|
|
21
22
|
const WIKI_INFRA_BASENAMES = new Set([SCHEMA_MD, INDEX_MD, LOG_MD]);
|
|
23
|
+
/**
|
|
24
|
+
* Extract the asset type from a canonical ref (`[origin//]type:name`) without
|
|
25
|
+
* throwing. Returns `undefined` for refs that have no `type:` prefix. Kept local
|
|
26
|
+
* and lenient — the per-type resolver validates the result against
|
|
27
|
+
* `getAssetTypes()`, so a bogus prefix here simply yields no convention.
|
|
28
|
+
*/
|
|
29
|
+
function refType(ref) {
|
|
30
|
+
if (!ref)
|
|
31
|
+
return undefined;
|
|
32
|
+
const body = ref.includes("//") ? ref.slice(ref.indexOf("//") + 2) : ref;
|
|
33
|
+
const colon = body.indexOf(":");
|
|
34
|
+
if (colon <= 0)
|
|
35
|
+
return undefined;
|
|
36
|
+
return body.slice(0, colon).trim() || undefined;
|
|
37
|
+
}
|
|
22
38
|
/**
|
|
23
39
|
* Resolve the standards context for a write target identified by its asset ref.
|
|
24
40
|
*
|
|
@@ -30,8 +46,23 @@ const WIKI_INFRA_BASENAMES = new Set([SCHEMA_MD, INDEX_MD, LOG_MD]);
|
|
|
30
46
|
export function resolveStandardsContext(ref, stashRoot) {
|
|
31
47
|
const wikiName = ref ? extractWikiNameFromRef(ref) : undefined;
|
|
32
48
|
if (!wikiName) {
|
|
33
|
-
// Non-wiki asset target → Feature B (stash
|
|
34
|
-
|
|
49
|
+
// Non-wiki asset target → Feature B (general stash standards) plus the
|
|
50
|
+
// per-type SOFT conventions layer (#646), type-scoped to the write target.
|
|
51
|
+
const general = resolveStashStandards(stashRoot);
|
|
52
|
+
const type = refType(ref);
|
|
53
|
+
// A non-empty body here guarantees `type` is a `getAssetTypes()`-validated
|
|
54
|
+
// string (the resolver returns "" otherwise).
|
|
55
|
+
const typeConventions = type ? resolveTypeConventions(stashRoot, type) : "";
|
|
56
|
+
if (!typeConventions || !type)
|
|
57
|
+
return general;
|
|
58
|
+
// Soft, type-scoped guidance — clearly labeled and kept separate from the
|
|
59
|
+
// HARD (validator-enforced) rules that `authoringRulesForType` injects
|
|
60
|
+
// downstream. These facts are advice only; they never weaken the gate.
|
|
61
|
+
const softSection = [
|
|
62
|
+
`# ${typeConventionRef(type)} (soft per-type conventions — guidance, not enforced)`,
|
|
63
|
+
typeConventions,
|
|
64
|
+
].join("\n");
|
|
65
|
+
return general ? `${general}\n\n${softSection}` : softSection;
|
|
35
66
|
}
|
|
36
67
|
// Wiki target. Extract the page path after `wiki:<name>/`.
|
|
37
68
|
const prefix = `wiki:${wikiName}/`;
|
|
@@ -21,6 +21,13 @@ import { parseFrontmatter } from "../asset/frontmatter.js";
|
|
|
21
21
|
const STANDARD_CATEGORIES = new Set(["convention", "meta"]);
|
|
22
22
|
/** Directory (under the stash root) where `fact` assets live. */
|
|
23
23
|
const FACTS_SUBDIR = "facts";
|
|
24
|
+
/**
|
|
25
|
+
* Per-type SOFT convention facts (`facts/conventions/assets/<type>.md`, #646)
|
|
26
|
+
* are surfaced **type-scoped** through `resolveTypeConventions`, so they must
|
|
27
|
+
* NOT leak into this un-type-scoped general layer (authoring a `command` must
|
|
28
|
+
* not pull the `skill` convention). Excluded by relative path (POSIX form).
|
|
29
|
+
*/
|
|
30
|
+
const TYPE_CONVENTIONS_REL = "conventions/assets/";
|
|
24
31
|
/**
|
|
25
32
|
* Recursively collect `.md` files under `dir` in stable (sorted) enumeration
|
|
26
33
|
* order. Returns absolute paths. Missing dir → `[]`.
|
|
@@ -59,6 +66,11 @@ export function resolveStashStandards(stashRoot) {
|
|
|
59
66
|
const factsRoot = path.join(stashRoot, FACTS_SUBDIR);
|
|
60
67
|
const sections = [];
|
|
61
68
|
for (const absPath of collectMarkdownFiles(factsRoot)) {
|
|
69
|
+
// Per-type SOFT conventions are delivered type-scoped (#646); skip them
|
|
70
|
+
// here so they never leak un-type-scoped into every authoring flow.
|
|
71
|
+
const relPosix = path.relative(factsRoot, absPath).split(path.sep).join("/");
|
|
72
|
+
if (relPosix.startsWith(TYPE_CONVENTIONS_REL))
|
|
73
|
+
continue;
|
|
62
74
|
let raw;
|
|
63
75
|
try {
|
|
64
76
|
raw = fs.readFileSync(absPath, "utf8");
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
+
/**
|
|
5
|
+
* Resolve **per-type SOFT authoring conventions** (#646) — the third and final
|
|
6
|
+
* authoring-guidance layer:
|
|
7
|
+
*
|
|
8
|
+
* 1. HARD rules (validator-rejecting, code-sourced) → `authoringRulesForType()`
|
|
9
|
+
* (`src/core/authoring-rules.ts`, #645). Never editable; the gate enforces them.
|
|
10
|
+
* 2. General stash standards (cross-type naming/tag conventions) →
|
|
11
|
+
* `resolveStashStandards()` `category: convention|meta` facts (#642).
|
|
12
|
+
* 3. **Per-type SOFT conventions** (voice, structure, length *preference* for
|
|
13
|
+
* *this* asset type) → user-editable `facts/conventions/assets/<type>.md`
|
|
14
|
+
* (THIS module). Augments the built-in `TYPE_HINTS` fallback for display.
|
|
15
|
+
*
|
|
16
|
+
* These facts are **soft only** — advice, not contract. They MUST NOT carry
|
|
17
|
+
* hard, validator-rejecting rules: a user editing or deleting one must never be
|
|
18
|
+
* able to weaken the authoring contract the gate enforces (#645 boundary).
|
|
19
|
+
*
|
|
20
|
+
* Selection is by a `getAssetTypes()`-validated basename: only
|
|
21
|
+
* `facts/conventions/assets/<known-type>.md` resolves. Read directly from disk
|
|
22
|
+
* (no index rebuild); any missing dir/file, unknown type, or read error degrades
|
|
23
|
+
* to `""` and never throws.
|
|
24
|
+
*/
|
|
25
|
+
import fs from "node:fs";
|
|
26
|
+
import path from "node:path";
|
|
27
|
+
import { getAssetTypes } from "../asset/asset-spec.js";
|
|
28
|
+
import { parseFrontmatter } from "../asset/frontmatter.js";
|
|
29
|
+
/** Sub-path (under the stash root) for per-type SOFT convention facts. */
|
|
30
|
+
export const TYPE_CONVENTIONS_SUBDIR = path.join("facts", "conventions", "assets");
|
|
31
|
+
/** The `fact:` ref prefix for a per-type convention, e.g. `fact:conventions/assets/skill`. */
|
|
32
|
+
export function typeConventionRef(type) {
|
|
33
|
+
return `fact:conventions/assets/${type}`;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Read the SOFT authoring-convention body for asset type `type`, if a stash
|
|
37
|
+
* owner has authored `facts/conventions/assets/<type>.md`.
|
|
38
|
+
*
|
|
39
|
+
* @returns the trimmed markdown body (frontmatter stripped), or `""` when the
|
|
40
|
+
* type is unknown, the file is absent, or anything goes wrong.
|
|
41
|
+
*/
|
|
42
|
+
export function resolveTypeConventions(stashRoot, type) {
|
|
43
|
+
if (!stashRoot || !type)
|
|
44
|
+
return "";
|
|
45
|
+
// Basename MUST be a known asset type — never resolve an arbitrary file.
|
|
46
|
+
if (!getAssetTypes().includes(type))
|
|
47
|
+
return "";
|
|
48
|
+
const abs = path.join(stashRoot, TYPE_CONVENTIONS_SUBDIR, `${type}.md`);
|
|
49
|
+
let raw;
|
|
50
|
+
try {
|
|
51
|
+
raw = fs.readFileSync(abs, "utf8");
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return ""; // missing dir/file or read error → degrade to empty
|
|
55
|
+
}
|
|
56
|
+
let body = "";
|
|
57
|
+
try {
|
|
58
|
+
body = parseFrontmatter(raw).content;
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
// Malformed frontmatter: fall back to the whole file (parseFrontmatter
|
|
62
|
+
// normally returns whole content as body, but guard defensively).
|
|
63
|
+
body = raw;
|
|
64
|
+
}
|
|
65
|
+
return body.trim();
|
|
66
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "akm-cli",
|
|
3
|
-
"version": "0.9.0-beta.
|
|
3
|
+
"version": "0.9.0-beta.39",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "akm (Agent Knowledge Management) — A package manager for AI agent skills, commands, tools, and knowledge. Works with Claude Code, OpenCode, Cursor, and any AI coding assistant.",
|
|
6
6
|
"keywords": [
|