akm-cli 0.9.0-beta.49 → 0.9.0-beta.50
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/dist/assets/profiles/default.json +9 -4
- package/dist/assets/profiles/frequent.json +1 -1
- package/dist/assets/profiles/memory-focus.json +1 -1
- package/dist/assets/profiles/quick.json +1 -1
- package/dist/assets/profiles/synthesize.json +15 -0
- package/dist/assets/profiles/thorough.json +1 -1
- package/dist/assets/prompts/graph-extract-user-prompt.md +1 -1
- package/dist/commands/improve/consolidate/eligibility.js +17 -0
- package/dist/commands/improve/consolidate.js +8 -2
- package/dist/commands/improve/improve-profiles.js +2 -0
- package/dist/commands/improve/improve.js +2 -1
- package/dist/commands/improve/loop-stages.js +4 -0
- package/dist/commands/improve/preparation.js +7 -4
- package/dist/commands/improve/recombine.js +33 -19
- package/dist/commands/improve/reflect.js +6 -5
- package/dist/commands/proposal/drain.js +1 -1
- package/dist/core/config/config-schema.js +22 -0
- package/dist/core/config/config.js +8 -3
- package/dist/core/improve-types.js +11 -8
- package/dist/core/state-db.js +22 -6
- package/dist/indexer/graph/graph-extraction.js +13 -1
- package/dist/llm/graph-extract.js +7 -1
- package/dist/scripts/migrate-storage.js +18 -3
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +6 -1
- package/package.json +2 -2
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
{
|
|
2
|
-
"description": "Standard improve pass — all sub-processes
|
|
2
|
+
"description": "Standard improve pass — all sub-processes + sustaining proactive lane.",
|
|
3
3
|
"processes": {
|
|
4
4
|
"reflect": {
|
|
5
5
|
"enabled": true,
|
|
6
|
+
"limit": 25,
|
|
6
7
|
"allowedTypes": ["agent", "command", "knowledge", "lesson", "memory", "skill", "wiki", "workflow"]
|
|
7
8
|
},
|
|
8
|
-
"distill": { "enabled": true, "allowedTypes": ["memory"] },
|
|
9
|
-
"consolidate": { "enabled": true, "allowedTypes": ["memory"] },
|
|
9
|
+
"distill": { "enabled": true, "allowedTypes": ["memory"], "requirePlannedRefs": true },
|
|
10
|
+
"consolidate": { "enabled": true, "allowedTypes": ["memory"], "minPoolSize": 500 },
|
|
10
11
|
"memoryInference": { "enabled": true },
|
|
11
12
|
"graphExtraction": { "enabled": true },
|
|
12
|
-
"
|
|
13
|
+
"extract": { "enabled": true, "triage": { "enabled": true, "minScore": 2 } },
|
|
14
|
+
"proactiveMaintenance": { "enabled": true, "dueDays": 30, "maxPerRun": 15 },
|
|
15
|
+
"triage": { "enabled": false, "applyMode": "queue", "policy": "personal-stash" },
|
|
16
|
+
"recombine": { "enabled": false },
|
|
17
|
+
"procedural": { "enabled": false }
|
|
13
18
|
},
|
|
14
19
|
"sync": { "enabled": true, "push": true }
|
|
15
20
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"consolidate": { "enabled": false },
|
|
7
7
|
"memoryInference": { "enabled": true },
|
|
8
8
|
"graphExtraction": { "enabled": true },
|
|
9
|
-
"extract": { "enabled": true, "minNewSessions": 3 },
|
|
9
|
+
"extract": { "enabled": true, "minNewSessions": 3, "triage": { "enabled": true, "minScore": 2 } },
|
|
10
10
|
"triage": { "enabled": false }
|
|
11
11
|
},
|
|
12
12
|
"sync": { "enabled": true, "push": true }
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"description": "Synthesis-only — cross-episodic recombination (#609). Opt-in periodic pass; all generative/extract passes off. (Procedural compilation (#615) is held OFF until cross-project scoping lands — it over-fits one-off sequences, 0% accept.)",
|
|
3
|
+
"processes": {
|
|
4
|
+
"reflect": { "enabled": false },
|
|
5
|
+
"distill": { "enabled": false },
|
|
6
|
+
"consolidate": { "enabled": false },
|
|
7
|
+
"memoryInference": { "enabled": false },
|
|
8
|
+
"graphExtraction": { "enabled": false },
|
|
9
|
+
"extract": { "enabled": false },
|
|
10
|
+
"triage": { "enabled": false },
|
|
11
|
+
"recombine": { "enabled": true },
|
|
12
|
+
"procedural": { "enabled": false }
|
|
13
|
+
},
|
|
14
|
+
"sync": { "enabled": true, "push": true }
|
|
15
|
+
}
|
|
@@ -2,7 +2,7 @@ Extract entities and relations from the asset body below.
|
|
|
2
2
|
|
|
3
3
|
Rules:
|
|
4
4
|
- Output ONLY a JSON object: {"entities": ["Entity One", ...], "relations": [{"from": "A", "to": "B", "type": "uses"}, ...]}.
|
|
5
|
-
- Entities are short, canonical noun phrases (project names, services, tools, people, file/
|
|
5
|
+
- Entities are short, canonical noun phrases (project names, services, tools, people, technical concepts). Do NOT emit file or directory paths (anything containing "/" or "\") — they are dropped downstream.
|
|
6
6
|
- Relations connect two entities that both appear in the entities array.
|
|
7
7
|
- "type" is a short verb phrase (e.g. "uses", "depends on", "owns", "documents"). Optional; omit when unsure.
|
|
8
8
|
- Drop pleasantries, meta-commentary, and timestamps.
|
|
@@ -9,6 +9,23 @@ import { hasHotCaptureMode } from "../../proposal/validators/proposal-quality-va
|
|
|
9
9
|
export function isConsolidationEligibleMemoryName(name) {
|
|
10
10
|
return !name.endsWith(".derived");
|
|
11
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* #632 — AKM session-capture telemetry memories: auto-generated session-end
|
|
14
|
+
* checkpoints named `<harness>-session-<YYYYMMDD>-<id>` or
|
|
15
|
+
* `<harness>-checkpoint-<YYYYMMDD…>-<id>`, carrying an embedded
|
|
16
|
+
* `akm_memory_kind: session_checkpoint` metadata block. Their bodies are
|
|
17
|
+
* pipeline bookkeeping, not durable knowledge, so the improve passes exclude
|
|
18
|
+
* them from their pools (recombine, consolidate). The `\d{8}` datestamp anchor
|
|
19
|
+
* is what distinguishes a capture name from a durable memory that merely
|
|
20
|
+
* MENTIONS session/checkpoint (e.g. `akm-plugins-session-end-extract-hook`,
|
|
21
|
+
* `session-checkpoint-lint-skips`), which stay in the pool.
|
|
22
|
+
*
|
|
23
|
+
* Lives here (the eligibility module) rather than in recombine so both
|
|
24
|
+
* recombine and consolidate can reuse it without a circular import.
|
|
25
|
+
*/
|
|
26
|
+
export function isSessionCaptureMemoryName(name) {
|
|
27
|
+
return /-(session|checkpoint)-\d{8}/.test(name);
|
|
28
|
+
}
|
|
12
29
|
/**
|
|
13
30
|
* Returns true when the memory file has `captureMode: hot` in its frontmatter.
|
|
14
31
|
*
|
|
@@ -41,8 +41,8 @@ import { normalizeUpdatedField, sanitizeMergedContent } from "./consolidate/sani
|
|
|
41
41
|
export { normalizeUpdatedField, sanitizeMergedContent, stripOuterCodeFence } from "./consolidate/sanitize.js";
|
|
42
42
|
// Eligibility / safety predicates live in ./consolidate/eligibility. Imported
|
|
43
43
|
// for internal guard use; the two public predicates are re-exported.
|
|
44
|
-
import { consolidateGuardStatus, isConsolidationEligibleMemoryName, isHotCapturedMemory, } from "./consolidate/eligibility.js";
|
|
45
|
-
export { isConsolidationEligibleMemoryName, isHotCapturedMemory } from "./consolidate/eligibility.js";
|
|
44
|
+
import { consolidateGuardStatus, isConsolidationEligibleMemoryName, isHotCapturedMemory, isSessionCaptureMemoryName, } from "./consolidate/eligibility.js";
|
|
45
|
+
export { isConsolidationEligibleMemoryName, isHotCapturedMemory, isSessionCaptureMemoryName, } from "./consolidate/eligibility.js";
|
|
46
46
|
// Plan parsing / merging (pure op-reconciliation algebra) lives in
|
|
47
47
|
// ./consolidate/merge. Imported for internal use; mergePlans re-exported.
|
|
48
48
|
import { isValidOp, mergePlans } from "./consolidate/merge.js";
|
|
@@ -2036,6 +2036,10 @@ function loadMemoriesForSource(source, stashDir, warnings) {
|
|
|
2036
2036
|
return path.resolve(e.stashDir) === path.resolve(source);
|
|
2037
2037
|
})
|
|
2038
2038
|
.filter((e) => isConsolidationEligibleMemoryName(e.entry.name))
|
|
2039
|
+
// #632 — exclude session-capture telemetry (checkpoints) from the
|
|
2040
|
+
// consolidation pool, mirroring recombine. Their bodies are pipeline
|
|
2041
|
+
// bookkeeping, so consolidating them burns LLM calls on pure noise.
|
|
2042
|
+
.filter((e) => !isSessionCaptureMemoryName(e.entry.name))
|
|
2039
2043
|
// Skip stale DB entries whose file was deleted by a prior run but not yet
|
|
2040
2044
|
// re-indexed. Without this guard the deleted file's ref appears in chunks
|
|
2041
2045
|
// sent to the LLM, which then proposes a second delete → delete_failed
|
|
@@ -2069,6 +2073,8 @@ function loadMemoriesForSource(source, stashDir, warnings) {
|
|
|
2069
2073
|
const name = fname.replace(/\.md$/, "");
|
|
2070
2074
|
if (!isConsolidationEligibleMemoryName(name))
|
|
2071
2075
|
continue;
|
|
2076
|
+
if (isSessionCaptureMemoryName(name))
|
|
2077
|
+
continue; // #632 — skip telemetry checkpoints
|
|
2072
2078
|
memories.push({ name, filePath, description: "", tags: [], stashDir: fsStashDir });
|
|
2073
2079
|
}
|
|
2074
2080
|
}
|
|
@@ -8,6 +8,7 @@ import profileFrequent from "../../assets/profiles/frequent.json" with { type: "
|
|
|
8
8
|
import profileGraphRefresh from "../../assets/profiles/graph-refresh.json" with { type: "json" };
|
|
9
9
|
import profileMemoryFocus from "../../assets/profiles/memory-focus.json" with { type: "json" };
|
|
10
10
|
import profileQuick from "../../assets/profiles/quick.json" with { type: "json" };
|
|
11
|
+
import profileSynthesize from "../../assets/profiles/synthesize.json" with { type: "json" };
|
|
11
12
|
import profileThorough from "../../assets/profiles/thorough.json" with { type: "json" };
|
|
12
13
|
import { parseAssetRef } from "../../core/asset/asset-ref.js";
|
|
13
14
|
import { warn } from "../../core/warn.js";
|
|
@@ -31,6 +32,7 @@ const BUILTIN_PROFILES = {
|
|
|
31
32
|
frequent: profileFrequent,
|
|
32
33
|
consolidate: profileConsolidate,
|
|
33
34
|
catchup: profileCatchup,
|
|
35
|
+
synthesize: profileSynthesize,
|
|
34
36
|
};
|
|
35
37
|
/**
|
|
36
38
|
* Default enabled-state for known improve processes when neither the user
|
|
@@ -869,7 +869,7 @@ function emitImproveCompletedEvent(result, durations, eventsCtx) {
|
|
|
869
869
|
// Coarse audit buckets, derived from the SAME classifyImproveAction the
|
|
870
870
|
// persisted metrics_json uses (state-db.ts#computeImproveRunMetrics) so the
|
|
871
871
|
// emitted event and the stored row can never disagree.
|
|
872
|
-
const classCounts = { accepted: 0, rejected: 0, error: 0, noop: 0 };
|
|
872
|
+
const classCounts = { accepted: 0, rejected: 0, skipped: 0, error: 0, noop: 0 };
|
|
873
873
|
for (const action of result.actions ?? []) {
|
|
874
874
|
classCounts[classifyImproveAction(action.mode)] += 1;
|
|
875
875
|
// Per-variant counters for the event metadata. The default arm makes any
|
|
@@ -936,6 +936,7 @@ function emitImproveCompletedEvent(result, durations, eventsCtx) {
|
|
|
936
936
|
reflectGuardRejectedActions: actionCounts.reflectGuardRejected,
|
|
937
937
|
acceptedActions: classCounts.accepted,
|
|
938
938
|
rejectedActions: classCounts.rejected,
|
|
939
|
+
skippedActions: classCounts.skipped,
|
|
939
940
|
noopActions: classCounts.noop,
|
|
940
941
|
reflectsWithErrorContext: result.reflectsWithErrorContext ?? 0,
|
|
941
942
|
coverageGapCount: result.coverageGaps?.length ?? 0,
|
|
@@ -214,6 +214,10 @@ export async function runImproveLoopStage(args) {
|
|
|
214
214
|
...(reflectErrors.length > 0 ? { avoidPatterns: [...reflectErrors] } : {}),
|
|
215
215
|
agentProcess: options.agentProcess ?? "reflect",
|
|
216
216
|
eventSource: "improve",
|
|
217
|
+
// #639 — resolve the low-value filter from the ACTIVE improve profile
|
|
218
|
+
// (default off when unset), so the running profile decides instead of
|
|
219
|
+
// a hardcoded profiles.improve.default path.
|
|
220
|
+
lowValueFilter: improveProfile.processes?.reflect?.lowValueFilter?.enabled === true,
|
|
217
221
|
...(reflectBudgetMs > 0 ? { timeoutMs: reflectBudgetMs } : {}),
|
|
218
222
|
...(reflectProfileRunner ? { runner: reflectProfileRunner } : {}),
|
|
219
223
|
// Attribution: carry the eligibility lane so reflect stamps it on
|
|
@@ -19,7 +19,7 @@ import { akmLint } from "../lint/index.js";
|
|
|
19
19
|
import { getProposal, listProposals } from "../proposal/validators/proposals.js";
|
|
20
20
|
import { runSchemaRepairPass } from "../sources/schema-repair.js";
|
|
21
21
|
import { computeThresholdAutoTune, gateDecisionsToSamples, summarizeCalibration, } from "./calibration.js";
|
|
22
|
-
import { akmConsolidate } from "./consolidate.js";
|
|
22
|
+
import { akmConsolidate, isSessionCaptureMemoryName } from "./consolidate.js";
|
|
23
23
|
// Eligibility / candidate-selection predicates live in ./eligibility.
|
|
24
24
|
import { buildLatestFeedbackTsMap, buildLatestProposalTsMap, buildUtilityMap, dedupeRefs, findAssetFilePath, isDistillCandidateRef, isLessonCandidate, isSignalDeltaEligible, } from "./eligibility.js";
|
|
25
25
|
import { akmExtract, countNewExtractCandidates } from "./extract.js";
|
|
@@ -1139,7 +1139,10 @@ export async function runImprovePreparationStage(args) {
|
|
|
1139
1139
|
withStateDb((dbForHighSalience) => {
|
|
1140
1140
|
const effectiveLimit = options.limit ?? 10;
|
|
1141
1141
|
const highSalienceCap = Math.max(1, Math.floor(effectiveLimit * 0.1));
|
|
1142
|
-
|
|
1142
|
+
// #632/#4 — session-capture telemetry (checkpoints) must never consume
|
|
1143
|
+
// the scarce high-salience budget. Even with a content-scored row, these
|
|
1144
|
+
// are pipeline bookkeeping, not assets worth reflecting/rewriting.
|
|
1145
|
+
const candidates = noFeedbackCandidates.filter((r) => !proactiveAndRetrievalSet.has(r.ref) && !isSessionCaptureMemoryName(parseAssetRef(r.ref).name));
|
|
1143
1146
|
for (const r of candidates) {
|
|
1144
1147
|
if (highSalienceRefs.length >= highSalienceCap)
|
|
1145
1148
|
break;
|
|
@@ -1465,7 +1468,7 @@ export async function runImprovePreparationStage(args) {
|
|
|
1465
1468
|
// stash-wide), but it is the most faithful comparison possible at cutover and
|
|
1466
1469
|
// allows the top-200→below-500 forgetting guard to fire if the formula change
|
|
1467
1470
|
// dramatically reorders the candidate pool.
|
|
1468
|
-
// See docs/
|
|
1471
|
+
// See docs/archive/improve-reconciliation-plan.md §WS-1 step 7 — the stash-wide
|
|
1469
1472
|
// ordering was unreconstructable (no prior state.db snapshot), so this candidate-
|
|
1470
1473
|
// pool partial reconstruction is the documented resolution for the first-run case.
|
|
1471
1474
|
// Emit `improve_salience_first_run` to mark the cutover moment and include the
|
|
@@ -1507,7 +1510,7 @@ export async function runImprovePreparationStage(args) {
|
|
|
1507
1510
|
// Limitation: this covers only the current-run candidate pool, not the full
|
|
1508
1511
|
// stash. The stash-wide ordering was never persisted (asset_salience is a new
|
|
1509
1512
|
// WS-1 table), so this is the most faithful comparison possible at cutover.
|
|
1510
|
-
// See docs/
|
|
1513
|
+
// See docs/archive/improve-reconciliation-plan.md §WS-1 step 7.
|
|
1511
1514
|
const reconstructedOldScores = new Map();
|
|
1512
1515
|
for (const ref of salienceMap.keys()) {
|
|
1513
1516
|
const utility = utilityMap.get(ref) ?? 0;
|
|
@@ -53,7 +53,7 @@ import { resolveImproveProcessRunnerFromProfile, runnerIsLlm } from "../../integ
|
|
|
53
53
|
import { chatCompletion } from "../../llm/client.js";
|
|
54
54
|
import { validateProposalFrontmatter } from "../proposal/validators/proposal-quality-validators.js";
|
|
55
55
|
import { archiveProposal, createProposal, isProposalSkipped, listProposals } from "../proposal/validators/proposals.js";
|
|
56
|
-
import { isConsolidationEligibleMemoryName } from "./consolidate.js";
|
|
56
|
+
import { isConsolidationEligibleMemoryName, isSessionCaptureMemoryName } from "./consolidate.js";
|
|
57
57
|
const RECOMBINE_SYSTEM_PROMPT = recombineSystemPrompt;
|
|
58
58
|
const DEFAULT_MIN_CLUSTER_SIZE = 3;
|
|
59
59
|
const DEFAULT_MAX_CLUSTERS_PER_RUN = 5;
|
|
@@ -174,24 +174,10 @@ const JUNK_ENTITY_NORMS = new Set([
|
|
|
174
174
|
"metadata",
|
|
175
175
|
"status",
|
|
176
176
|
]);
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
* `akm_memory_kind: session_checkpoint` metadata block. Their bodies are
|
|
182
|
-
* pipeline bookkeeping, so the graph extracts their metadata FIELDS
|
|
183
|
-
* (`session_checkpoint`, `harness`, `buffered observations`, `tool_*_observed`,
|
|
184
|
-
* …) as ENTITIES — which then dominate entity clustering as bland, stash-wide
|
|
185
|
-
* mega-buckets (the #632 symptom, just under an `entity:` signature). They are
|
|
186
|
-
* session telemetry, not durable knowledge to generalize, so recombine excludes
|
|
187
|
-
* them from its pool. The `\d{8}` datestamp anchor is what distinguishes a
|
|
188
|
-
* capture name from a durable memory that merely MENTIONS session/checkpoint
|
|
189
|
-
* (e.g. `akm-plugins-session-end-extract-hook`, `session-checkpoint-lint-skips`),
|
|
190
|
-
* which stay in the pool.
|
|
191
|
-
*/
|
|
192
|
-
export function isSessionCaptureMemoryName(name) {
|
|
193
|
-
return /-(session|checkpoint)-\d{8}/.test(name);
|
|
194
|
-
}
|
|
177
|
+
// #632 — `isSessionCaptureMemoryName` now lives in ./consolidate/eligibility so
|
|
178
|
+
// both recombine and consolidate can reuse it without a circular import. It is
|
|
179
|
+
// re-exported here for back-compat (existing importers + tests).
|
|
180
|
+
export { isSessionCaptureMemoryName };
|
|
195
181
|
/**
|
|
196
182
|
* #632 — an entity carries no clustering signal (and must be skipped) when it is
|
|
197
183
|
* a generic extraction artefact (session / structured-log bookkeeping), a raw
|
|
@@ -602,6 +588,34 @@ export async function akmRecombine(opts) {
|
|
|
602
588
|
break;
|
|
603
589
|
}
|
|
604
590
|
clustersFormed += 1;
|
|
591
|
+
// #9 — promotion is terminal. If this cluster Jaccard-matches a
|
|
592
|
+
// hypothesis that was ALREADY promoted, generating again can at best
|
|
593
|
+
// re-queue a redundant `type: hypothesis` for a settled ref (promotion
|
|
594
|
+
// gates on !alreadyPromoted below), so the LLM output is wasted. Skip the
|
|
595
|
+
// per-cluster call entirely. The decay sweep still spares the row — it
|
|
596
|
+
// matches a present cluster via `presentClusters` (independent of
|
|
597
|
+
// `seenThisRun`), so the promotion record is preserved.
|
|
598
|
+
if (stateDb) {
|
|
599
|
+
const promotedMatch = findMatchingRecombineHypothesis(stateDb, {
|
|
600
|
+
signature: cluster.signature,
|
|
601
|
+
memberKey: recombineMemberKey(cluster),
|
|
602
|
+
minOverlap: DEFAULT_RECOMBINE_OVERLAP,
|
|
603
|
+
});
|
|
604
|
+
if (promotedMatch?.hypothesis_ref &&
|
|
605
|
+
getRecombineHypothesis(stateDb, promotedMatch.hypothesis_ref)?.promoted_at != null) {
|
|
606
|
+
appendEvent({
|
|
607
|
+
eventType: "recombine_invoked",
|
|
608
|
+
ref: promotedMatch.hypothesis_ref,
|
|
609
|
+
metadata: {
|
|
610
|
+
signal: cluster.signature,
|
|
611
|
+
memberCount: cluster.members.length,
|
|
612
|
+
outcome: "skipped_promoted",
|
|
613
|
+
sourceRun,
|
|
614
|
+
},
|
|
615
|
+
}, opts.ctx);
|
|
616
|
+
continue;
|
|
617
|
+
}
|
|
618
|
+
}
|
|
605
619
|
const prompt = buildClusterPrompt(cluster, standardsContext);
|
|
606
620
|
const raw = await llmFn(prompt);
|
|
607
621
|
const generalization = parseGeneralization(raw);
|
|
@@ -1275,11 +1275,12 @@ export async function akmReflect(options = {}) {
|
|
|
1275
1275
|
// (new-asset proposals have nothing to diff against).
|
|
1276
1276
|
if (assetContent !== undefined) {
|
|
1277
1277
|
const changeKind = classifyReflectChange(assetContent, payload.content);
|
|
1278
|
-
// 'low-value' is config-gated (#639)
|
|
1279
|
-
//
|
|
1280
|
-
//
|
|
1281
|
-
//
|
|
1282
|
-
|
|
1278
|
+
// 'low-value' is config-gated (#639). DEFAULT OFF — absent = byte-identical
|
|
1279
|
+
// pre-#639 behaviour (low-value treated the same as substantive). Resolved
|
|
1280
|
+
// by the caller from the ACTIVE improve profile's
|
|
1281
|
+
// `processes.reflect.lowValueFilter.enabled` and passed via options, so the
|
|
1282
|
+
// running profile (not a hardcoded `profiles.improve.default`) decides.
|
|
1283
|
+
const lowValueFilterEnabled = options.lowValueFilter === true;
|
|
1283
1284
|
const isDeferred = changeKind === "noop" || changeKind === "cosmetic" || (changeKind === "low-value" && lowValueFilterEnabled);
|
|
1284
1285
|
if (isDeferred) {
|
|
1285
1286
|
const subreason = changeKind === "noop"
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* the engine behind `akm proposal drain` and (later) the `triage` improve
|
|
10
10
|
* pre-pass.
|
|
11
11
|
*
|
|
12
|
-
* Design (see docs/
|
|
12
|
+
* Design (see docs/archive/proposal-triage-implementation-plan.md):
|
|
13
13
|
* - Reuses `listProposals` (no source filter — generator filtering is
|
|
14
14
|
* in-memory) and the `akmProposalAccept` / `akmProposalReject` wrappers from
|
|
15
15
|
* `proposal.ts` so the standard `promoted` / `rejected` events are emitted.
|
|
@@ -125,6 +125,10 @@ export const AgentProfileConfigSchema = z
|
|
|
125
125
|
args: z.array(z.string()).optional(),
|
|
126
126
|
workspace: z.string().min(1).optional(),
|
|
127
127
|
model: z.string().min(1).optional(),
|
|
128
|
+
// Per-call timeout (ms) used when a command does not pass an explicit
|
|
129
|
+
// timeout (e.g. `akm wiki ingest` without `--timeout-ms`). null = no
|
|
130
|
+
// timeout. Honored by resolveAgentProfile (integrations/agent/config.ts).
|
|
131
|
+
timeoutMs: z.union([positiveInt, z.null()]).optional(),
|
|
128
132
|
})
|
|
129
133
|
.passthrough();
|
|
130
134
|
// ── Improve profile / process ──────────────────────────────────────────────
|
|
@@ -201,6 +205,23 @@ export const ImproveProcessConfigSchema = z
|
|
|
201
205
|
// high-signal-first sweep). Unset = process all eligible (current
|
|
202
206
|
// behavior). Only meaningful on `graphExtraction`.
|
|
203
207
|
topN: positiveInt.optional(),
|
|
208
|
+
// graphExtraction process: full-corpus scan. When true, graph extraction
|
|
209
|
+
// runs on ALL stash files instead of only files touched by actionable refs
|
|
210
|
+
// in the current run. Used by the `graph-refresh` built-in profile / a
|
|
211
|
+
// scheduled weekly task. Only meaningful on `graphExtraction`.
|
|
212
|
+
fullScan: z.boolean().optional(),
|
|
213
|
+
// #626 — extract process: pre-LLM heuristic triage gate. When enabled, a
|
|
214
|
+
// deterministic scorer decides BEFORE the extraction LLM call whether a
|
|
215
|
+
// session carries enough signal to be worth extracting; low-signal sessions
|
|
216
|
+
// are skipped at zero LLM cost. Default OFF. Only meaningful on `extract`.
|
|
217
|
+
// `minScore` is the minimum total heuristic score to PASS (default 2).
|
|
218
|
+
triage: z
|
|
219
|
+
.object({
|
|
220
|
+
enabled: z.boolean().optional(),
|
|
221
|
+
minScore: z.number().min(0).optional(),
|
|
222
|
+
})
|
|
223
|
+
.passthrough()
|
|
224
|
+
.optional(),
|
|
204
225
|
// MemoryInference process: minimum pending memory count to run the pass.
|
|
205
226
|
minPendingCount: z.number().int().min(0).optional(),
|
|
206
227
|
// Extract process: minimum number of new (unseen, in-window) candidate
|
|
@@ -377,6 +398,7 @@ const ImproveProfileProcessesSchema = z
|
|
|
377
398
|
consolidate: ImproveProcessConfigSchema.optional(),
|
|
378
399
|
memoryInference: ImproveProcessConfigSchema.optional(),
|
|
379
400
|
graphExtraction: ImproveProcessConfigSchema.optional(),
|
|
401
|
+
extract: ImproveProcessConfigSchema.optional(),
|
|
380
402
|
validation: ImproveProcessConfigSchema.optional(),
|
|
381
403
|
triage: ImproveProcessConfigSchema.optional(),
|
|
382
404
|
proactiveMaintenance: ImproveProcessConfigSchema.optional(),
|
|
@@ -284,9 +284,14 @@ export function saveConfig(config) {
|
|
|
284
284
|
const dir = path.dirname(configPath);
|
|
285
285
|
fs.mkdirSync(dir, { recursive: true });
|
|
286
286
|
const sanitized = sanitizeConfigForWrite(config);
|
|
287
|
-
// Final validation gate before bytes hit disk.
|
|
288
|
-
//
|
|
289
|
-
//
|
|
287
|
+
// Final validation gate before bytes hit disk. Runs the FULL schema —
|
|
288
|
+
// including the cross-field superRefine guards (removed `feedbackDistillation`
|
|
289
|
+
// process key, `defaultWriteTarget` resolution, writable npm/website sources)
|
|
290
|
+
// and all type/enum/range checks — so an `akm config set` (leaf OR object
|
|
291
|
+
// form) cannot persist a guard-violating or mistyped value. NOTE: unknown
|
|
292
|
+
// keys are intentionally NOT rejected here — object schemas are `.passthrough()`
|
|
293
|
+
// so cross-version skew round-trips (see config-schema.ts header); the lenient
|
|
294
|
+
// tolerance is by design, not an oversight.
|
|
290
295
|
const parseResult = AkmConfigSchema.safeParse(sanitized);
|
|
291
296
|
if (!parseResult.success) {
|
|
292
297
|
const lines = parseResult.error.issues.map((i) => ` - ${i.path.join(".") || "(root)"}: ${i.message}`).join("\n");
|
|
@@ -11,16 +11,18 @@ import { assertNever } from "./assert.js";
|
|
|
11
11
|
*
|
|
12
12
|
* Buckets:
|
|
13
13
|
* - `accepted` — a write/content-authoring action succeeded.
|
|
14
|
-
* - `
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
14
|
+
* - `skipped` — the ref was GATED OUT before any content was produced: a
|
|
15
|
+
* cooldown, a signal-delta/eligibility skip, or a distill pool-delta skip.
|
|
16
|
+
* These are NOT rejections of produced content — they are the run declining
|
|
17
|
+
* to act on a ref it had no new reason to touch, and they scale with the
|
|
18
|
+
* whole indexed-ref pool (~13k/run), so folding them into `rejected` made the
|
|
19
|
+
* "accept rate" meaningless (deep-tuning analysis 2026-06-29, finding #1).
|
|
20
|
+
* - `rejected` — the run PRODUCED a change and a content-policy guard then
|
|
21
|
+
* rejected it (`reflect-guard-rejected`). This is the genuine value-rejection
|
|
22
|
+
* signal; it is small and meaningful, no longer drowned by gated skips.
|
|
21
23
|
* - `error` — the action failed (LLM/runtime error).
|
|
22
24
|
* - `noop` — bookkeeping that is neither a write nor a rejection (memory-prune);
|
|
23
|
-
* intentionally counted in none of the
|
|
25
|
+
* intentionally counted in none of the numeric buckets.
|
|
24
26
|
*
|
|
25
27
|
* The `default: assertNever(mode)` arm makes any future union variant a
|
|
26
28
|
* compile-time error here, forcing an explicit bucket choice.
|
|
@@ -35,6 +37,7 @@ export function classifyImproveAction(mode) {
|
|
|
35
37
|
case "reflect-cooldown":
|
|
36
38
|
case "reflect-skipped":
|
|
37
39
|
case "distill-skipped":
|
|
40
|
+
return "skipped";
|
|
38
41
|
case "reflect-guard-rejected":
|
|
39
42
|
return "rejected";
|
|
40
43
|
case "reflect-failed":
|
package/dist/core/state-db.js
CHANGED
|
@@ -485,7 +485,10 @@ function isRetryableBeginError(err) {
|
|
|
485
485
|
const msg = (err instanceof Error ? err.message : String(err)).toLowerCase();
|
|
486
486
|
return (msg.includes("within a transaction") ||
|
|
487
487
|
msg.includes("database is locked") ||
|
|
488
|
-
msg.includes("database table is locked")
|
|
488
|
+
msg.includes("database table is locked") ||
|
|
489
|
+
// Phantom BEGIN (see below) — synthesized when BEGIN IMMEDIATE returns
|
|
490
|
+
// without opening a transaction. Safe to retry: fn() has not run.
|
|
491
|
+
msg.includes("did not open a transaction"));
|
|
489
492
|
}
|
|
490
493
|
const WITH_IMMEDIATE_TX_MAX_ATTEMPTS = 5;
|
|
491
494
|
/** Portable synchronous sleep (works under both Bun and Node). */
|
|
@@ -499,6 +502,15 @@ export function withImmediateTransaction(db, fn) {
|
|
|
499
502
|
for (let attempt = 1; attempt <= WITH_IMMEDIATE_TX_MAX_ATTEMPTS; attempt++) {
|
|
500
503
|
try {
|
|
501
504
|
db.exec("BEGIN IMMEDIATE");
|
|
505
|
+
// bun:sqlite can return from BEGIN IMMEDIATE under writer contention WITHOUT
|
|
506
|
+
// actually opening a transaction (no throw). That phantom state otherwise
|
|
507
|
+
// surfaces as "cannot commit - no transaction is active" at COMMIT — AFTER
|
|
508
|
+
// fn() has already run in autocommit, so its writes escaped the intended
|
|
509
|
+
// serialization (the concurrent proposal-queue race). Detect it here, before
|
|
510
|
+
// fn(), and route it through the same retry path as a contended BEGIN.
|
|
511
|
+
if (!db.inTransaction) {
|
|
512
|
+
throw new Error("BEGIN IMMEDIATE did not open a transaction (phantom contention state)");
|
|
513
|
+
}
|
|
502
514
|
}
|
|
503
515
|
catch (err) {
|
|
504
516
|
lastBeginErr = err;
|
|
@@ -740,15 +752,16 @@ export function computeImproveRunMetrics(result) {
|
|
|
740
752
|
const actionsCount = actions.length;
|
|
741
753
|
let acceptedCount = 0;
|
|
742
754
|
let rejectedCount = 0;
|
|
755
|
+
let skippedCount = 0;
|
|
743
756
|
let autoAcceptedCount = 0;
|
|
744
757
|
let errorCount = 0;
|
|
745
758
|
for (const action of actions) {
|
|
746
759
|
// Bucketing delegated to the shared classifyImproveAction so this aggregate
|
|
747
760
|
// and the improve_completed event in improve.ts can never disagree, and so a
|
|
748
|
-
// new union variant is a compile error rather than a silent drop.
|
|
749
|
-
//
|
|
750
|
-
//
|
|
751
|
-
// intentionally counted in none of the
|
|
761
|
+
// new union variant is a compile error rather than a silent drop. Gated skips
|
|
762
|
+
// (cooldown / signal-delta / distill pool-delta) bucket to "skipped", NOT
|
|
763
|
+
// "rejected" — only a guard-rejected produced change is a true rejection.
|
|
764
|
+
// "noop" (memory-prune) is intentionally counted in none of the buckets.
|
|
752
765
|
switch (classifyImproveAction(action.mode)) {
|
|
753
766
|
case "accepted":
|
|
754
767
|
acceptedCount++;
|
|
@@ -756,6 +769,9 @@ export function computeImproveRunMetrics(result) {
|
|
|
756
769
|
case "rejected":
|
|
757
770
|
rejectedCount++;
|
|
758
771
|
break;
|
|
772
|
+
case "skipped":
|
|
773
|
+
skippedCount++;
|
|
774
|
+
break;
|
|
759
775
|
case "error":
|
|
760
776
|
errorCount++;
|
|
761
777
|
break;
|
|
@@ -769,7 +785,7 @@ export function computeImproveRunMetrics(result) {
|
|
|
769
785
|
}
|
|
770
786
|
// Add gate-promoted count from the unified PostPhaseAutoAcceptGate (all phases).
|
|
771
787
|
autoAcceptedCount += result.gateAutoAcceptedCount ?? 0;
|
|
772
|
-
return { plannedCount, actionsCount, acceptedCount, rejectedCount, autoAcceptedCount, errorCount };
|
|
788
|
+
return { plannedCount, actionsCount, acceptedCount, rejectedCount, skippedCount, autoAcceptedCount, errorCount };
|
|
773
789
|
}
|
|
774
790
|
/**
|
|
775
791
|
* Insert a single improve-run row into `improve_runs`. Uses parameterised SQL.
|
|
@@ -837,10 +837,14 @@ export function rankCandidatesByUtility(db, candidates, _stashRoot) {
|
|
|
837
837
|
*
|
|
838
838
|
* Inferred-child memories (frontmatter `inferred: true`) are skipped — they
|
|
839
839
|
* are already derived summaries, with no additional internal graph structure worth
|
|
840
|
-
* extracting.
|
|
840
|
+
* extracting. Session-capture telemetry checkpoints are skipped too (see below).
|
|
841
841
|
*
|
|
842
842
|
* Exported for direct unit testing.
|
|
843
843
|
*/
|
|
844
|
+
// #632 — canonical session-capture name shape, mirrored from
|
|
845
|
+
// isSessionCaptureMemoryName (src/commands/improve/consolidate/eligibility.ts).
|
|
846
|
+
// Inlined to avoid an improve-layer import from the indexer layer.
|
|
847
|
+
const SESSION_CAPTURE_NAME_RE = /-(session|checkpoint)-\d{8}/;
|
|
844
848
|
export function collectEligibleFiles(stashRoot, includeTypes = [...DEFAULT_GRAPH_EXTRACTION_INCLUDE_TYPES]) {
|
|
845
849
|
const out = [];
|
|
846
850
|
for (const rawType of includeTypes) {
|
|
@@ -866,6 +870,14 @@ export function collectEligibleFiles(stashRoot, includeTypes = [...DEFAULT_GRAPH
|
|
|
866
870
|
// graph to extract from a single-fact body.
|
|
867
871
|
if (type === "memory" && parsed.data.inferred === true)
|
|
868
872
|
continue;
|
|
873
|
+
// #632 — skip session-capture telemetry checkpoints (named
|
|
874
|
+
// `<harness>-(session|checkpoint)-<YYYYMMDD>-<id>`). Their bodies are
|
|
875
|
+
// pipeline bookkeeping; graph-extracting them lifts metadata fields as
|
|
876
|
+
// entities that then dominate clustering as bland stash-wide buckets.
|
|
877
|
+
// Mirrors isSessionCaptureMemoryName (consolidate/eligibility.ts); inlined
|
|
878
|
+
// here to keep the indexer layer free of an improve-layer import.
|
|
879
|
+
if (type === "memory" && SESSION_CAPTURE_NAME_RE.test(path.basename(filePath, ".md")))
|
|
880
|
+
continue;
|
|
869
881
|
const body = parsed.content.trim();
|
|
870
882
|
if (!body)
|
|
871
883
|
continue;
|
|
@@ -314,7 +314,13 @@ function parseGraphExtraction(raw) {
|
|
|
314
314
|
if (!normalized)
|
|
315
315
|
continue;
|
|
316
316
|
const normalizedKey = normalized.toLowerCase();
|
|
317
|
-
|
|
317
|
+
// Drop generic/empty entities AND raw file/dir paths (anything with a
|
|
318
|
+
// path separator) — the prompt no longer asks for them and isJunkEntity
|
|
319
|
+
// discards them downstream, so emitting them is pure waste/junk (#632).
|
|
320
|
+
if (!/[a-z0-9]/i.test(normalized) ||
|
|
321
|
+
GENERIC_ENTITIES.has(normalizedKey) ||
|
|
322
|
+
normalized.includes("/") ||
|
|
323
|
+
normalized.includes("\\")) {
|
|
318
324
|
filteredGenericEntities += 1;
|
|
319
325
|
continue;
|
|
320
326
|
}
|
|
@@ -8986,6 +8986,7 @@ function classifyImproveAction(mode) {
|
|
|
8986
8986
|
case "reflect-cooldown":
|
|
8987
8987
|
case "reflect-skipped":
|
|
8988
8988
|
case "distill-skipped":
|
|
8989
|
+
return "skipped";
|
|
8989
8990
|
case "reflect-guard-rejected":
|
|
8990
8991
|
return "rejected";
|
|
8991
8992
|
case "reflect-failed":
|
|
@@ -9701,7 +9702,7 @@ function insertProposalIfAbsent(db, proposal, stashDir) {
|
|
|
9701
9702
|
}
|
|
9702
9703
|
function isRetryableBeginError(err) {
|
|
9703
9704
|
const msg = (err instanceof Error ? err.message : String(err)).toLowerCase();
|
|
9704
|
-
return msg.includes("within a transaction") || msg.includes("database is locked") || msg.includes("database table is locked");
|
|
9705
|
+
return msg.includes("within a transaction") || msg.includes("database is locked") || msg.includes("database table is locked") || msg.includes("did not open a transaction");
|
|
9705
9706
|
}
|
|
9706
9707
|
function sleepSyncMs(ms) {
|
|
9707
9708
|
if (ms <= 0)
|
|
@@ -9713,6 +9714,9 @@ function withImmediateTransaction(db, fn) {
|
|
|
9713
9714
|
for (let attempt = 1;attempt <= WITH_IMMEDIATE_TX_MAX_ATTEMPTS; attempt++) {
|
|
9714
9715
|
try {
|
|
9715
9716
|
db.exec("BEGIN IMMEDIATE");
|
|
9717
|
+
if (!db.inTransaction) {
|
|
9718
|
+
throw new Error("BEGIN IMMEDIATE did not open a transaction (phantom contention state)");
|
|
9719
|
+
}
|
|
9716
9720
|
} catch (err) {
|
|
9717
9721
|
lastBeginErr = err;
|
|
9718
9722
|
if (isRetryableBeginError(err) && attempt < WITH_IMMEDIATE_TX_MAX_ATTEMPTS) {
|
|
@@ -9846,6 +9850,7 @@ function computeImproveRunMetrics(result) {
|
|
|
9846
9850
|
const actionsCount = actions.length;
|
|
9847
9851
|
let acceptedCount = 0;
|
|
9848
9852
|
let rejectedCount = 0;
|
|
9853
|
+
let skippedCount = 0;
|
|
9849
9854
|
let autoAcceptedCount = 0;
|
|
9850
9855
|
let errorCount = 0;
|
|
9851
9856
|
for (const action of actions) {
|
|
@@ -9856,6 +9861,9 @@ function computeImproveRunMetrics(result) {
|
|
|
9856
9861
|
case "rejected":
|
|
9857
9862
|
rejectedCount++;
|
|
9858
9863
|
break;
|
|
9864
|
+
case "skipped":
|
|
9865
|
+
skippedCount++;
|
|
9866
|
+
break;
|
|
9859
9867
|
case "error":
|
|
9860
9868
|
errorCount++;
|
|
9861
9869
|
break;
|
|
@@ -9867,7 +9875,7 @@ function computeImproveRunMetrics(result) {
|
|
|
9867
9875
|
autoAcceptedCount++;
|
|
9868
9876
|
}
|
|
9869
9877
|
autoAcceptedCount += result.gateAutoAcceptedCount ?? 0;
|
|
9870
|
-
return { plannedCount, actionsCount, acceptedCount, rejectedCount, autoAcceptedCount, errorCount };
|
|
9878
|
+
return { plannedCount, actionsCount, acceptedCount, rejectedCount, skippedCount, autoAcceptedCount, errorCount };
|
|
9871
9879
|
}
|
|
9872
9880
|
function recordImproveRun(db, input) {
|
|
9873
9881
|
const metricsObj = input.metrics ?? computeImproveRunMetrics(input.result);
|
|
@@ -15850,7 +15858,8 @@ var init_config_schema = __esm(() => {
|
|
|
15850
15858
|
bin: exports_external.string().min(1).optional(),
|
|
15851
15859
|
args: exports_external.array(exports_external.string()).optional(),
|
|
15852
15860
|
workspace: exports_external.string().min(1).optional(),
|
|
15853
|
-
model: exports_external.string().min(1).optional()
|
|
15861
|
+
model: exports_external.string().min(1).optional(),
|
|
15862
|
+
timeoutMs: exports_external.union([positiveInt, exports_external.null()]).optional()
|
|
15854
15863
|
}).passthrough();
|
|
15855
15864
|
ImproveProcessConfigSchema = exports_external.object({
|
|
15856
15865
|
enabled: exports_external.boolean().optional(),
|
|
@@ -15878,6 +15887,11 @@ var init_config_schema = __esm(() => {
|
|
|
15878
15887
|
dueDays: exports_external.number().int().min(0).optional(),
|
|
15879
15888
|
maxPerRun: positiveInt.optional(),
|
|
15880
15889
|
topN: positiveInt.optional(),
|
|
15890
|
+
fullScan: exports_external.boolean().optional(),
|
|
15891
|
+
triage: exports_external.object({
|
|
15892
|
+
enabled: exports_external.boolean().optional(),
|
|
15893
|
+
minScore: exports_external.number().min(0).optional()
|
|
15894
|
+
}).passthrough().optional(),
|
|
15881
15895
|
minPendingCount: exports_external.number().int().min(0).optional(),
|
|
15882
15896
|
minNewSessions: exports_external.number().int().min(0).optional(),
|
|
15883
15897
|
maxSessionsPerRun: exports_external.number().int().min(0).optional(),
|
|
@@ -15939,6 +15953,7 @@ var init_config_schema = __esm(() => {
|
|
|
15939
15953
|
consolidate: ImproveProcessConfigSchema.optional(),
|
|
15940
15954
|
memoryInference: ImproveProcessConfigSchema.optional(),
|
|
15941
15955
|
graphExtraction: ImproveProcessConfigSchema.optional(),
|
|
15956
|
+
extract: ImproveProcessConfigSchema.optional(),
|
|
15942
15957
|
validation: ImproveProcessConfigSchema.optional(),
|
|
15943
15958
|
triage: ImproveProcessConfigSchema.optional(),
|
|
15944
15959
|
proactiveMaintenance: ImproveProcessConfigSchema.optional(),
|
|
@@ -8713,6 +8713,7 @@ function classifyImproveAction(mode) {
|
|
|
8713
8713
|
case "reflect-cooldown":
|
|
8714
8714
|
case "reflect-skipped":
|
|
8715
8715
|
case "distill-skipped":
|
|
8716
|
+
return "skipped";
|
|
8716
8717
|
case "reflect-guard-rejected":
|
|
8717
8718
|
return "rejected";
|
|
8718
8719
|
case "reflect-failed":
|
|
@@ -9160,6 +9161,7 @@ function computeImproveRunMetrics(result) {
|
|
|
9160
9161
|
const actionsCount = actions.length;
|
|
9161
9162
|
let acceptedCount = 0;
|
|
9162
9163
|
let rejectedCount = 0;
|
|
9164
|
+
let skippedCount = 0;
|
|
9163
9165
|
let autoAcceptedCount = 0;
|
|
9164
9166
|
let errorCount = 0;
|
|
9165
9167
|
for (const action of actions) {
|
|
@@ -9170,6 +9172,9 @@ function computeImproveRunMetrics(result) {
|
|
|
9170
9172
|
case "rejected":
|
|
9171
9173
|
rejectedCount++;
|
|
9172
9174
|
break;
|
|
9175
|
+
case "skipped":
|
|
9176
|
+
skippedCount++;
|
|
9177
|
+
break;
|
|
9173
9178
|
case "error":
|
|
9174
9179
|
errorCount++;
|
|
9175
9180
|
break;
|
|
@@ -9181,7 +9186,7 @@ function computeImproveRunMetrics(result) {
|
|
|
9181
9186
|
autoAcceptedCount++;
|
|
9182
9187
|
}
|
|
9183
9188
|
autoAcceptedCount += result.gateAutoAcceptedCount ?? 0;
|
|
9184
|
-
return { plannedCount, actionsCount, acceptedCount, rejectedCount, autoAcceptedCount, errorCount };
|
|
9189
|
+
return { plannedCount, actionsCount, acceptedCount, rejectedCount, skippedCount, autoAcceptedCount, errorCount };
|
|
9185
9190
|
}
|
|
9186
9191
|
function recordImproveRun(db, input) {
|
|
9187
9192
|
const metricsObj = input.metrics ?? computeImproveRunMetrics(input.result);
|
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.50",
|
|
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": [
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"lint:devto-posts:fix": "bun scripts/lint-devto-posts.ts --fix",
|
|
70
70
|
"publish:devto": "npx -y @sinedied/devto-cli push \"docs/posts/**/*.md\" --token \"$DEVTO_TOKEN\" --repo \"$GITHUB_REPOSITORY\" --branch \"${GITHUB_REF_NAME:-main}\" --reconcile",
|
|
71
71
|
"release:check": "./tests/release-check.sh",
|
|
72
|
-
"lint": "bunx biome check src/ tests/ && bun scripts/lint-tests-isolation.ts && bun scripts/lint-license-headers.ts && bun scripts/lint-runtime-boundary.ts && bun scripts/lint-repository-sql.ts",
|
|
72
|
+
"lint": "bunx biome check src/ tests/ && bun scripts/lint-tests-isolation.ts && bun scripts/lint-license-headers.ts && bun scripts/lint-runtime-boundary.ts && bun scripts/lint-repository-sql.ts && bun scripts/gen-config-schema.ts --check",
|
|
73
73
|
"lint:runtime-boundary": "bun scripts/lint-runtime-boundary.ts",
|
|
74
74
|
"lint:tests-isolation": "bun scripts/lint-tests-isolation.ts",
|
|
75
75
|
"lint:fix": "bunx biome check --write src/ tests/",
|