akm-cli 0.9.0-beta.5 → 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/CHANGELOG.md +709 -0
- 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/consolidate-system.md +23 -0
- package/dist/assets/prompts/contradiction-judge.md +33 -0
- package/dist/assets/prompts/distill-knowledge-system.md +22 -0
- package/dist/assets/prompts/distill-lesson-system.md +36 -0
- package/dist/assets/prompts/extract-session.md +6 -2
- package/dist/assets/prompts/graph-extract-system.md +1 -0
- package/dist/assets/prompts/graph-extract-user-prompt.md +1 -1
- package/dist/assets/prompts/memory-infer-system.md +1 -0
- package/dist/assets/prompts/memory-infer-user.md +5 -0
- package/dist/assets/prompts/metadata-enhance-system.md +1 -0
- package/dist/assets/prompts/procedural-system.md +44 -0
- package/dist/assets/prompts/recombine-system.md +40 -0
- package/dist/assets/prompts/staleness-detect-system.md +6 -0
- package/dist/assets/prompts/validate-summary-judge.md +1 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +38 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +38 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +39 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +40 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +43 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +38 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +43 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +40 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +43 -0
- package/dist/assets/templates/html/health.html +281 -111
- package/dist/assets/wiki/ingest-workflow-template.md +17 -10
- package/dist/cli/shared.js +28 -0
- package/dist/cli.js +15 -5
- package/dist/commands/agent/agent-dispatch.js +2 -2
- package/dist/commands/agent/agent-support.js +0 -7
- package/dist/commands/agent/contribute-cli.js +17 -4
- package/dist/commands/env/env-cli.js +16 -24
- package/dist/commands/env/secret-cli.js +12 -20
- package/dist/commands/feedback-cli.js +15 -6
- package/dist/commands/graph/graph-cli.js +5 -13
- package/dist/commands/graph/graph.js +76 -72
- package/dist/commands/health/checks.js +48 -0
- package/dist/commands/health/html-report.js +422 -80
- package/dist/commands/health.js +386 -9
- package/dist/commands/improve/calibration.js +161 -0
- package/dist/commands/improve/consolidate/chunking.js +141 -0
- package/dist/commands/improve/consolidate/eligibility.js +81 -0
- package/dist/commands/improve/consolidate/merge.js +145 -0
- package/dist/commands/improve/consolidate/sanitize.js +231 -0
- package/dist/commands/{lint.js → improve/consolidate/types.js} +1 -1
- package/dist/commands/improve/consolidate.js +635 -660
- package/dist/commands/improve/dedup.js +482 -0
- package/dist/commands/improve/distill.js +159 -69
- package/dist/commands/improve/eligibility.js +434 -0
- package/dist/commands/improve/encoding-salience.js +205 -0
- package/dist/commands/improve/extract-cli.js +124 -2
- package/dist/commands/improve/extract-prompt.js +39 -2
- package/dist/commands/improve/extract-watch.js +140 -0
- package/dist/commands/improve/extract.js +389 -40
- package/dist/commands/improve/feedback-valence.js +54 -0
- package/dist/commands/improve/homeostatic.js +467 -0
- package/dist/commands/improve/improve-auto-accept.js +109 -6
- package/dist/commands/improve/improve-cli.js +35 -60
- package/dist/commands/improve/improve-profiles.js +14 -0
- package/dist/commands/improve/improve-result-file.js +5 -23
- package/dist/commands/improve/improve-session.js +58 -0
- package/dist/commands/improve/improve.js +485 -2498
- package/dist/commands/improve/locks.js +154 -0
- package/dist/commands/improve/loop-stages.js +1083 -0
- package/dist/commands/improve/memory/memory-contradiction-detect.js +23 -28
- package/dist/commands/improve/outcome-loop.js +256 -0
- package/dist/commands/improve/preparation.js +1966 -0
- package/dist/commands/improve/proactive-maintenance.js +115 -0
- package/dist/commands/improve/procedural.js +418 -0
- package/dist/commands/improve/recombine.js +813 -0
- package/dist/commands/improve/reflect-noise.js +0 -0
- package/dist/commands/improve/reflect.js +183 -40
- package/dist/commands/improve/salience.js +438 -0
- package/dist/commands/improve/triage.js +93 -0
- package/dist/commands/lint/agent-linter.js +19 -24
- package/dist/commands/lint/base-linter.js +173 -60
- package/dist/commands/lint/command-linter.js +19 -24
- package/dist/commands/lint/env-key-rules.js +34 -1
- package/dist/commands/lint/fact-linter.js +39 -0
- package/dist/commands/lint/index.js +31 -13
- package/dist/commands/lint/memory-linter.js +1 -1
- package/dist/commands/lint/registry.js +7 -2
- package/dist/commands/lint/task-linter.js +3 -3
- package/dist/commands/lint/workflow-linter.js +26 -1
- package/dist/commands/proposal/drain-policies.js +5 -0
- package/dist/commands/proposal/drain.js +43 -50
- package/dist/commands/proposal/proposal-cli.js +21 -31
- package/dist/commands/proposal/proposal.js +5 -0
- package/dist/commands/proposal/propose.js +7 -2
- package/dist/commands/proposal/validators/proposal-quality-validators.js +9 -8
- package/dist/commands/proposal/validators/proposals.js +189 -63
- package/dist/commands/read/curate.js +414 -94
- package/dist/commands/read/knowledge.js +2 -2
- package/dist/commands/read/search-cli.js +7 -0
- package/dist/commands/read/search.js +1 -0
- package/dist/commands/read/show.js +67 -2
- package/dist/commands/sources/init.js +36 -9
- package/dist/commands/sources/installed-stashes.js +5 -1
- package/dist/commands/sources/schema-repair.js +13 -1
- package/dist/commands/sources/self-update.js +2 -2
- package/dist/commands/sources/stash-cli.js +28 -40
- package/dist/commands/sources/stash-skeleton.js +23 -8
- package/dist/commands/tasks/tasks-cli.js +19 -27
- package/dist/commands/tasks/tasks.js +1 -1
- package/dist/commands/wiki-cli.js +21 -35
- package/dist/core/asset/asset-registry.js +2 -0
- package/dist/core/asset/asset-spec.js +14 -0
- package/dist/core/asset/frontmatter.js +166 -167
- package/dist/core/asset/markdown.js +8 -0
- package/dist/core/authoring-rules.js +92 -0
- package/dist/core/common.js +0 -5
- package/dist/core/config/config-schema.js +340 -56
- package/dist/core/config/config-types.js +3 -3
- package/dist/core/config/config.js +28 -7
- package/dist/core/events.js +3 -7
- package/dist/core/improve-types.js +11 -8
- package/dist/core/logs-db.js +10 -66
- package/dist/core/parse.js +36 -16
- package/dist/core/paths.js +3 -0
- package/dist/core/standards/resolve-standards-context.js +87 -0
- package/dist/core/standards/resolve-stash-standards.js +99 -0
- package/dist/core/standards/resolve-type-conventions.js +66 -0
- package/dist/core/state/migrations.js +714 -0
- package/dist/core/state-db.js +525 -474
- package/dist/indexer/db/db.js +439 -247
- package/dist/indexer/db/graph-db.js +129 -86
- package/dist/indexer/ensure-index.js +152 -17
- package/dist/indexer/graph/graph-boost.js +51 -41
- package/dist/indexer/graph/graph-extraction.js +218 -4
- package/dist/indexer/index-writer-lock.js +99 -0
- package/dist/indexer/indexer.js +123 -221
- package/dist/indexer/passes/dir-staleness.js +114 -0
- package/dist/indexer/passes/memory-inference.js +10 -3
- package/dist/indexer/passes/staleness-detect.js +2 -5
- package/dist/indexer/search/db-search.js +15 -4
- package/dist/indexer/search/ranking-contributors.js +22 -0
- package/dist/indexer/search/ranking.js +4 -0
- package/dist/indexer/search/search-source.js +10 -24
- package/dist/indexer/search/semantic-status.js +4 -0
- package/dist/indexer/walk/matchers.js +9 -0
- package/dist/integrations/agent/config.js +6 -53
- package/dist/integrations/agent/index.js +2 -18
- package/dist/integrations/agent/prompts.js +74 -8
- package/dist/integrations/agent/runner-dispatch.js +59 -0
- package/dist/integrations/harnesses/claude/session-log.js +11 -1
- package/dist/integrations/harnesses/index.js +2 -3
- package/dist/integrations/harnesses/opencode/session-log.js +173 -3
- package/dist/integrations/harnesses/opencode-sdk/index.js +2 -2
- package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +0 -2
- package/dist/integrations/session-logs/index.js +16 -0
- package/dist/llm/client.js +45 -15
- package/dist/llm/embedder.js +42 -3
- package/dist/llm/embedders/deterministic.js +66 -0
- package/dist/llm/embedders/local.js +66 -2
- package/dist/llm/feature-gate.js +8 -4
- package/dist/llm/graph-extract.js +67 -44
- package/dist/llm/memory-infer.js +38 -30
- package/dist/llm/metadata-enhance.js +44 -31
- package/dist/llm/structured-call.js +49 -0
- package/dist/output/context.js +5 -5
- package/dist/output/renderers.js +73 -1
- package/dist/output/shapes/curate.js +14 -2
- package/dist/output/shapes/passthrough.js +0 -1
- package/dist/output/text/helpers.js +16 -1
- package/dist/registry/providers/skills-sh.js +21 -147
- package/dist/registry/providers/static-index.js +15 -157
- package/dist/registry/resolve.js +22 -9
- package/dist/runtime.js +25 -1
- package/dist/scripts/migrate-storage.js +2136 -1596
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +682 -433
- package/dist/setup/setup.js +29 -8
- package/dist/sources/providers/filesystem.js +0 -1
- package/dist/sources/providers/git-install.js +206 -0
- package/dist/sources/providers/git-provider.js +234 -0
- package/dist/sources/providers/git-stash.js +248 -0
- package/dist/sources/providers/git.js +10 -661
- package/dist/sources/providers/npm.js +2 -6
- package/dist/sources/providers/sync-from-ref.js +9 -1
- package/dist/sources/providers/tar-utils.js +16 -8
- package/dist/sources/providers/website.js +2 -3
- package/dist/sources/website-ingest.js +51 -9
- package/dist/sources/wiki-fetchers/registry.js +53 -0
- package/dist/sources/wiki-fetchers/youtube.js +239 -0
- package/dist/storage/database.js +45 -10
- package/dist/storage/managed-db.js +82 -0
- package/dist/storage/repositories/registry-cache.js +92 -0
- package/dist/storage/sqlite-pragmas.js +146 -0
- package/dist/tasks/backends/cron.js +1 -1
- package/dist/tasks/backends/launchd.js +1 -1
- package/dist/tasks/backends/schtasks.js +1 -1
- package/dist/tasks/{resolveAkmBin.js → resolve-akm-bin.js} +2 -2
- package/dist/tasks/runner.js +5 -13
- package/dist/wiki/wiki.js +37 -0
- package/dist/workflows/db.js +3 -4
- package/dist/workflows/runtime/runs.js +1 -117
- package/dist/workflows/runtime/workflow-asset-loader.js +125 -0
- package/dist/workflows/validate-summary.js +2 -7
- package/docs/data-and-telemetry.md +1 -0
- package/package.json +9 -7
- package/dist/commands/db-cli.js +0 -23
- package/dist/indexer/db/db-backup.js +0 -376
|
@@ -63,6 +63,11 @@ export const searchCommand = defineJsonCommand({
|
|
|
63
63
|
description: "Disable the automatic project-context ranking boost (also disabled by AKM_DISABLE_PROJECT_CONTEXT=1).",
|
|
64
64
|
default: false,
|
|
65
65
|
},
|
|
66
|
+
"include-sessions": {
|
|
67
|
+
type: "boolean",
|
|
68
|
+
description: "Include session assets (excluded from default search results via config.search.defaultExcludeTypes).",
|
|
69
|
+
default: false,
|
|
70
|
+
},
|
|
66
71
|
},
|
|
67
72
|
async run({ args }) {
|
|
68
73
|
const query = (args.query ?? "").trim();
|
|
@@ -79,6 +84,7 @@ export const searchCommand = defineJsonCommand({
|
|
|
79
84
|
const includeProposed = args["include-proposed"] === true;
|
|
80
85
|
const belief = parseBeliefFilterMode(typeof args.belief === "string" ? args.belief : undefined);
|
|
81
86
|
const noProjectContext = getHyphenatedBoolean(args, "no-project-context");
|
|
87
|
+
const includeSessions = getHyphenatedBoolean(args, "include-sessions");
|
|
82
88
|
// --no-project-context sets env so searchDatabase picks it up without
|
|
83
89
|
// threading the flag through the entire call stack.
|
|
84
90
|
if (noProjectContext)
|
|
@@ -91,6 +97,7 @@ export const searchCommand = defineJsonCommand({
|
|
|
91
97
|
filters,
|
|
92
98
|
includeProposed,
|
|
93
99
|
belief,
|
|
100
|
+
includeSessions,
|
|
94
101
|
eventSource: resolveEventSource(),
|
|
95
102
|
});
|
|
96
103
|
output("search", result);
|
|
@@ -108,6 +108,7 @@ export async function akmSearch(input) {
|
|
|
108
108
|
// Without this, the index (which spans every configured source)
|
|
109
109
|
// would leak hits from sources the caller did not request.
|
|
110
110
|
restrictToSources: namedSourceName !== undefined,
|
|
111
|
+
includeExcludedTypes: input.includeSessions === true,
|
|
111
112
|
});
|
|
112
113
|
const registryResult = source === "stash" ? undefined : await searchRegistry(query, { limit, registries: config.registries });
|
|
113
114
|
if (source === "stash") {
|
|
@@ -22,18 +22,22 @@ import { parseAssetRef } from "../../core/asset/asset-ref.js";
|
|
|
22
22
|
import { parseFrontmatter } from "../../core/asset/frontmatter.js";
|
|
23
23
|
import { META_DIR, parseMetaRef, resolveMetaFilePath } from "../../core/asset/stash-meta.js";
|
|
24
24
|
import { asNonEmptyString } from "../../core/common.js";
|
|
25
|
-
import { loadConfig } from "../../core/config/config.js";
|
|
25
|
+
import { getIndexPassConfig, loadConfig } from "../../core/config/config.js";
|
|
26
26
|
import { NotFoundError, rethrowIfTestIsolationError, UsageError } from "../../core/errors.js";
|
|
27
27
|
import { appendEvent, readEvents } from "../../core/events.js";
|
|
28
|
-
import { findEntryIdByRef } from "../../indexer/db/db.js";
|
|
28
|
+
import { closeDatabase, computeBodyHash, findEntryIdByRef, openExistingDatabase } from "../../indexer/db/db.js";
|
|
29
|
+
import { hasGraphData } from "../../indexer/db/graph-db.js";
|
|
29
30
|
import { ensureIndex } from "../../indexer/ensure-index.js";
|
|
30
31
|
import { listRelatedPathsForFile } from "../../indexer/graph/graph-boost.js";
|
|
32
|
+
import { extractGraphForSingleFile } from "../../indexer/graph/graph-extraction.js";
|
|
31
33
|
import { lookup } from "../../indexer/indexer.js";
|
|
32
34
|
import { buildEditHint, findSourceForPath, isEditable, resolveSourceEntries } from "../../indexer/search/search-source.js";
|
|
33
35
|
import { insertUsageEvent } from "../../indexer/usage/usage-events.js";
|
|
34
36
|
import { buildFileContext, buildRenderContext, getRenderer, runMatchers } from "../../indexer/walk/file-context.js";
|
|
35
37
|
import { resolveAssetPath } from "../../indexer/walk/path-resolver.js";
|
|
38
|
+
import { resolveIndexPassLLM } from "../../llm/index-passes.js";
|
|
36
39
|
import { resolveSourcesForOrigin } from "../../registry/origin-resolve.js";
|
|
40
|
+
import { resolveStorageLocations } from "../../storage/locations.js";
|
|
37
41
|
import { withIndexDb } from "../../storage/repositories/index-db.js";
|
|
38
42
|
// Eagerly import source providers to trigger self-registration.
|
|
39
43
|
import "../../sources/providers/index.js";
|
|
@@ -383,6 +387,15 @@ export async function showLocal(input) {
|
|
|
383
387
|
if (activeRun) {
|
|
384
388
|
fullResponse.activeRun = activeRun;
|
|
385
389
|
}
|
|
390
|
+
// #624-P3: opt-in inline graph extraction. Default OFF — when the flag is
|
|
391
|
+
// unset this whole block is skipped (no hasGraphData check, no LLM call), so
|
|
392
|
+
// behavior is byte-identical to today. When ON, it extracts graph data for an
|
|
393
|
+
// ungraphed asset, but ONLY when a model is configured (model-available
|
|
394
|
+
// guard) and ALWAYS bounded by a 30s timeout so `show` can never hang. Any
|
|
395
|
+
// timeout/model-unavailable/error path returns the response unchanged.
|
|
396
|
+
if (getIndexPassConfig(config.index, "graph")?.lazyGraphExtraction === true) {
|
|
397
|
+
await maybeExtractGraphInline(config, sourceStashDir, assetPath);
|
|
398
|
+
}
|
|
386
399
|
if (input.detail === "brief") {
|
|
387
400
|
return buildBriefResponse(fullResponse, assetPath);
|
|
388
401
|
}
|
|
@@ -391,6 +404,58 @@ export async function showLocal(input) {
|
|
|
391
404
|
}
|
|
392
405
|
return fullResponse;
|
|
393
406
|
}
|
|
407
|
+
/**
|
|
408
|
+
* #624-P3 — opt-in inline graph extraction for `akm show`. Best-effort and
|
|
409
|
+
* timeout-bounded: never throws, never hangs, never mutates the response.
|
|
410
|
+
*
|
|
411
|
+
* Preconditions (caller already checked the flag): a model must be configured
|
|
412
|
+
* (model-available guard via {@link resolveIndexPassLLM}) and the asset must be
|
|
413
|
+
* ungraphed ({@link hasGraphData}). Extraction races a 30s timeout so `show`
|
|
414
|
+
* cannot block on a slow provider; any timeout/error/missing-model path is
|
|
415
|
+
* swallowed and `show` returns its already-assembled response unchanged.
|
|
416
|
+
*/
|
|
417
|
+
async function maybeExtractGraphInline(config, sourceStashDir, assetPath) {
|
|
418
|
+
try {
|
|
419
|
+
// Model-available guard — no provider configured ⇒ silent skip, no LLM call.
|
|
420
|
+
if (!resolveIndexPassLLM("graph", config))
|
|
421
|
+
return;
|
|
422
|
+
let alreadyGraphed = false;
|
|
423
|
+
let bodyHash;
|
|
424
|
+
try {
|
|
425
|
+
const raw = fs.readFileSync(assetPath, "utf8");
|
|
426
|
+
bodyHash = computeBodyHash(parseFrontmatter(raw).content.trim());
|
|
427
|
+
}
|
|
428
|
+
catch {
|
|
429
|
+
return; // file gone/unreadable ⇒ nothing to extract
|
|
430
|
+
}
|
|
431
|
+
withIndexDb((db) => {
|
|
432
|
+
alreadyGraphed = hasGraphData(db, sourceStashDir, assetPath);
|
|
433
|
+
});
|
|
434
|
+
if (alreadyGraphed)
|
|
435
|
+
return;
|
|
436
|
+
// Open the db for the async extraction ourselves: `withIndexDb` is
|
|
437
|
+
// synchronous and would close the connection the instant the async fn
|
|
438
|
+
// returns its Promise (before extraction completes). Close it explicitly
|
|
439
|
+
// after the race settles instead.
|
|
440
|
+
const db = openExistingDatabase(resolveStorageLocations().indexDb);
|
|
441
|
+
let timer;
|
|
442
|
+
const timeout = new Promise((resolve) => {
|
|
443
|
+
timer = setTimeout(resolve, 30_000);
|
|
444
|
+
});
|
|
445
|
+
try {
|
|
446
|
+
await Promise.race([extractGraphForSingleFile(db, sourceStashDir, assetPath, bodyHash, { config }), timeout]);
|
|
447
|
+
}
|
|
448
|
+
finally {
|
|
449
|
+
if (timer)
|
|
450
|
+
clearTimeout(timer);
|
|
451
|
+
closeDatabase(db);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
catch (err) {
|
|
455
|
+
rethrowIfTestIsolationError(err);
|
|
456
|
+
// Any other failure: silently return the unchanged show response.
|
|
457
|
+
}
|
|
458
|
+
}
|
|
394
459
|
/**
|
|
395
460
|
* Minimal `show`: ref → indexer lookup → file contents. Used by callers that
|
|
396
461
|
* just need the raw file (e.g. clone, write-source) and don't want the full
|
|
@@ -53,6 +53,8 @@ function assertInitSandbox(stashDir, dirExplicitlyProvided) {
|
|
|
53
53
|
throw new ConfigError(`refusing to persist --dir stashDir to a temporary path while under test runner; set AKM_FORCE_INIT_TMP_STASH=1 if you really mean it (stashDir=${stashDir})`, "INIT_TMP_STASH_REFUSED");
|
|
54
54
|
}
|
|
55
55
|
export async function akmInit(options) {
|
|
56
|
+
const dirExplicitlyProvided = options?.dir != null;
|
|
57
|
+
const setDefault = options?.setDefault === true;
|
|
56
58
|
const stashDir = options?.dir ? path.resolve(options.dir) : getDefaultStashDir();
|
|
57
59
|
// Safety check (#473): refuse stashDir at /, $HOME, /etc, ~/.config, etc.
|
|
58
60
|
// Runs BEFORE any disk write — a fat-fingered `akm init --dir /` or
|
|
@@ -61,7 +63,7 @@ export async function akmInit(options) {
|
|
|
61
63
|
assertSafeStashDir(stashDir);
|
|
62
64
|
// Defense-in-depth: refuse to persist an explicit --dir /tmp/... stashDir
|
|
63
65
|
// to config under a test runner. Default HOME-resolved paths are exempt.
|
|
64
|
-
assertInitSandbox(stashDir,
|
|
66
|
+
assertInitSandbox(stashDir, dirExplicitlyProvided);
|
|
65
67
|
let created = false;
|
|
66
68
|
if (!fs.existsSync(stashDir)) {
|
|
67
69
|
fs.mkdirSync(stashDir, { recursive: true });
|
|
@@ -75,15 +77,40 @@ export async function akmInit(options) {
|
|
|
75
77
|
}
|
|
76
78
|
// Ensure the default stash is a local git repo (no remote required)
|
|
77
79
|
ensureGitRepo(stashDir);
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
//
|
|
80
|
+
// Run seeding UNCONDITIONALLY (not just when the stash was newly created) so
|
|
81
|
+
// re-running `akm init` on an existing stash backfills any missing skeleton
|
|
82
|
+
// files — the README, the per-type SOFT convention templates under
|
|
83
|
+
// facts/conventions/assets/, and the `.meta/index.md` orientation doc. Both
|
|
84
|
+
// helpers are absent-only: they never overwrite a file a user has edited.
|
|
85
|
+
copyStashSkeleton(stashDir);
|
|
86
|
+
scaffoldStashMeta(stashDir);
|
|
87
|
+
// Persist stashDir in config.json — but ONLY when the user is actually
|
|
88
|
+
// setting up / opting into a default. A bare `akm init --dir <secondary>`
|
|
89
|
+
// must NOT silently repoint the user's real default stash (the footgun
|
|
90
|
+
// documented in memory:akm-init-persists-stashdir-warning).
|
|
91
|
+
//
|
|
92
|
+
// Decision matrix — persist when ANY of:
|
|
93
|
+
// (a) no --dir provided → default HOME-resolved setup flow
|
|
94
|
+
// (b) --dir AND no existing stashDir in config → first-time bootstrap
|
|
95
|
+
// (c) --dir AND --set-default → explicit opt-in
|
|
96
|
+
// Otherwise (--dir + existing default + no --set-default) leave the default
|
|
97
|
+
// pointer alone; the target dir is still scaffolded above.
|
|
83
98
|
const configPath = getConfigPath();
|
|
84
99
|
const existing = loadUserConfig();
|
|
85
|
-
|
|
86
|
-
|
|
100
|
+
const existingStashDir = existing.stashDir;
|
|
101
|
+
const shouldPersist = !dirExplicitlyProvided || !existingStashDir || setDefault;
|
|
102
|
+
let defaultStashUpdated = false;
|
|
103
|
+
let previousStashDir;
|
|
104
|
+
if (shouldPersist) {
|
|
105
|
+
if (!existingStashDir || existingStashDir !== stashDir) {
|
|
106
|
+
saveConfig({ ...existing, stashDir });
|
|
107
|
+
defaultStashUpdated = true;
|
|
108
|
+
}
|
|
109
|
+
// else: already pointed here — no-op, no spurious rewrite.
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
// Default left untouched; surface it so the CLI can inform the user.
|
|
113
|
+
previousStashDir = existingStashDir;
|
|
87
114
|
}
|
|
88
115
|
// Ensure ripgrep is available (install to cache/bin if needed)
|
|
89
116
|
let ripgrep;
|
|
@@ -95,7 +122,7 @@ export async function akmInit(options) {
|
|
|
95
122
|
catch {
|
|
96
123
|
// Non-fatal: ripgrep is optional, search works without it
|
|
97
124
|
}
|
|
98
|
-
return { stashDir, created, configPath, ripgrep };
|
|
125
|
+
return { stashDir, created, configPath, defaultStashUpdated, previousStashDir, ripgrep };
|
|
99
126
|
}
|
|
100
127
|
/** Initialise `dir` as a git repository if it is not already one. */
|
|
101
128
|
function ensureGitRepo(dir) {
|
|
@@ -205,7 +205,11 @@ async function updateRegistryEntry(entry, force) {
|
|
|
205
205
|
const synced = await syncFromRef(entry.ref, { force });
|
|
206
206
|
const installedEntry = {
|
|
207
207
|
id: synced.id,
|
|
208
|
-
source
|
|
208
|
+
// Preserve the original source classification. syncFromRef() re-derives the
|
|
209
|
+
// source type from the ref scheme (e.g. "github:" → source: "github"), but
|
|
210
|
+
// an update should not reclassify an existing entry. A writable entry stored
|
|
211
|
+
// as source: "git" would fail config validation if rewritten to "github".
|
|
212
|
+
source: entry.source,
|
|
209
213
|
ref: synced.ref,
|
|
210
214
|
artifactUrl: synced.artifactUrl,
|
|
211
215
|
resolvedVersion: synced.resolvedVersion,
|
|
@@ -17,7 +17,9 @@ import path from "node:path";
|
|
|
17
17
|
import { parseAssetRef } from "../../core/asset/asset-ref.js";
|
|
18
18
|
import { assembleAsset } from "../../core/asset/asset-serialize.js";
|
|
19
19
|
import { parseFrontmatter } from "../../core/asset/frontmatter.js";
|
|
20
|
+
import { authoringRulesForType } from "../../core/authoring-rules.js";
|
|
20
21
|
import { appendEvent, readEvents } from "../../core/events.js";
|
|
22
|
+
import { resolveStandardsContext } from "../../core/standards/resolve-standards-context.js";
|
|
21
23
|
import { info, warn } from "../../core/warn.js";
|
|
22
24
|
import { resolveAssetPath } from "../../indexer/walk/path-resolver.js";
|
|
23
25
|
import { chatCompletion, parseEmbeddedJsonResponse } from "../../llm/client.js";
|
|
@@ -94,6 +96,16 @@ export async function runSchemaRepairPass(failures, options) {
|
|
|
94
96
|
const fieldList = missingFields.join(" and ");
|
|
95
97
|
info(`[improve] schema-repair ${failure.ref} (${fieldList})`);
|
|
96
98
|
const bodyPreview = (fm.content ?? raw).slice(0, 2000);
|
|
99
|
+
// Standards "rulebook" for this target — wiki schema (wiki page) or stash
|
|
100
|
+
// convention/meta facts (non-wiki asset); empty when neither fires or no
|
|
101
|
+
// stash dir is available. `resolveStandardsContext` dispatches on the ref.
|
|
102
|
+
const standardsContext = stashDir ? resolveStandardsContext(failure.ref, stashDir) : "";
|
|
103
|
+
const standardsSection = standardsContext.trim()
|
|
104
|
+
? `\n\nStandards to follow (the rulebook for this target):\n${standardsContext.trim()}`
|
|
105
|
+
: "";
|
|
106
|
+
const assetType = parseAssetRef(failure.ref).type;
|
|
107
|
+
const authoringRules = authoringRulesForType(assetType);
|
|
108
|
+
const authoringRulesSection = authoringRules ? `\n\n${authoringRules}` : "";
|
|
97
109
|
const llmResponse = await chatFn(llmConfig, [
|
|
98
110
|
{
|
|
99
111
|
role: "system",
|
|
@@ -101,7 +113,7 @@ export async function runSchemaRepairPass(failures, options) {
|
|
|
101
113
|
},
|
|
102
114
|
{
|
|
103
115
|
role: "user",
|
|
104
|
-
content: `Generate the missing frontmatter fields (${fieldList}) for this ${
|
|
116
|
+
content: `Generate the missing frontmatter fields (${fieldList}) for this ${assetType} asset. Return ONLY valid JSON like {"description": "...", "when_to_use": "..."}${standardsSection}${authoringRulesSection}\n\n${bodyPreview}`,
|
|
105
117
|
},
|
|
106
118
|
]);
|
|
107
119
|
const parsed = parseEmbeddedJsonResponse(llmResponse.trim());
|
|
@@ -306,8 +306,8 @@ export async function performUpgrade(check, opts) {
|
|
|
306
306
|
* The new binary's `akm index` does the work for us:
|
|
307
307
|
* 1. loadConfig() runs at startup — auto-migrates legacy `stashes` →
|
|
308
308
|
* `sources` if the on-disk config still uses the old key.
|
|
309
|
-
* 2. ensureSchema()
|
|
310
|
-
*
|
|
309
|
+
* 2. ensureSchema() converges index.db forward via its idempotent baseline
|
|
310
|
+
* schema + additive migrations (no destructive rebuild).
|
|
311
311
|
* 3. The full reindex repopulates entries + workflow_documents + FTS.
|
|
312
312
|
*/
|
|
313
313
|
function runPostUpgradeTasks(akmBin, opts) {
|
|
@@ -5,20 +5,20 @@
|
|
|
5
5
|
* Stash-lifecycle command cluster — the create/index/ingest/inspect verbs for
|
|
6
6
|
* the working stash and its index database: `akm init` (create the stash +
|
|
7
7
|
* persist stashDir), `akm index` (build/refresh the search index), `akm import`
|
|
8
|
-
* (ingest a knowledge doc/URL), `akm
|
|
9
|
-
*
|
|
8
|
+
* (ingest a knowledge doc/URL), and `akm info` (system capabilities + index
|
|
9
|
+
* stats).
|
|
10
10
|
* Extracted verbatim from src/cli.ts (WS6) so the God Module shrinks; the
|
|
11
|
-
* `main.subCommands.{init,index,import,
|
|
11
|
+
* `main.subCommands.{init,index,import,info}` keys and every subcommand's
|
|
12
12
|
* args/output shape stay byte-identical.
|
|
13
13
|
*
|
|
14
14
|
* These share no private helper with any command still inline in cli.ts — every
|
|
15
15
|
* dependency is already exported from a shared module (core/paths, core/warn,
|
|
16
16
|
* core/errors, core/events, output/context, cli/shared, cli/parse-args, plus the
|
|
17
|
-
* per-command implementations in ./init, ./indexer, ./info, ./
|
|
17
|
+
* per-command implementations in ./init, ./indexer, ./info, ./knowledge,
|
|
18
18
|
* ./core/asset-create, ./core/common), so the cluster moves with zero hoisting.
|
|
19
19
|
*
|
|
20
20
|
* The leaf handlers whose body is a plain `runWithJsonErrors(...) + output(...)`
|
|
21
|
-
* (`init`, `import`, `info
|
|
21
|
+
* (`init`, `import`, `info`) are migrated onto
|
|
22
22
|
* `defineJsonCommand`, which emits the same JSON envelope (stdout/stderr/
|
|
23
23
|
* exit-code) as the inline form. `index` keeps a plain `defineCommand` wrapping
|
|
24
24
|
* `runWithJsonErrors` because its body owns a spinner, an AbortController, and
|
|
@@ -27,17 +27,17 @@
|
|
|
27
27
|
import path from "node:path";
|
|
28
28
|
import * as p from "@clack/prompts";
|
|
29
29
|
import { defineCommand } from "citty";
|
|
30
|
-
import { hasSubcommand } from "../../cli/parse-args.js";
|
|
31
30
|
import { defineJsonCommand, output, runWithJsonErrors } from "../../cli/shared.js";
|
|
32
31
|
import { assertFlatAssetName } from "../../core/asset/asset-create.js";
|
|
33
32
|
import { isHttpUrl } from "../../core/common.js";
|
|
33
|
+
import { loadConfig } from "../../core/config/config.js";
|
|
34
34
|
import { UsageError } from "../../core/errors.js";
|
|
35
35
|
import { appendEvent } from "../../core/events.js";
|
|
36
36
|
import { getCacheDir } from "../../core/paths.js";
|
|
37
37
|
import { clearLogFile, info, isVerbose, setLogFile } from "../../core/warn.js";
|
|
38
|
+
import { resolveWriteTarget } from "../../core/write-source.js";
|
|
38
39
|
import { akmIndex } from "../../indexer/indexer.js";
|
|
39
40
|
import { getHyphenatedBoolean, getOutputMode, parseFlagValue } from "../../output/context.js";
|
|
40
|
-
import { akmDbBackups } from "../db-cli.js";
|
|
41
41
|
import { readKnowledgeInput, writeMarkdownAsset } from "../read/knowledge.js";
|
|
42
42
|
import { assembleInfo } from "./info.js";
|
|
43
43
|
import { akmInit } from "./init.js";
|
|
@@ -48,12 +48,20 @@ export const initCommand = defineJsonCommand({
|
|
|
48
48
|
},
|
|
49
49
|
args: {
|
|
50
50
|
dir: { type: "string", description: "Custom stash directory path (default: ~/akm)" },
|
|
51
|
+
"set-default": {
|
|
52
|
+
type: "boolean",
|
|
53
|
+
description: "Make --dir the default stash (write stashDir to config.json). Without this, `akm init --dir X` scaffolds X but leaves your existing default stash unchanged.",
|
|
54
|
+
default: false,
|
|
55
|
+
},
|
|
51
56
|
},
|
|
52
57
|
async run({ args }) {
|
|
53
58
|
// Accept both historical spellings for backwards compatibility with
|
|
54
59
|
// older docs/scripts that used `--stashDir`.
|
|
55
60
|
const legacyDir = parseFlagValue(process.argv, "--stashDir") ?? parseFlagValue(process.argv, "--stash-dir");
|
|
56
|
-
const result = await akmInit({
|
|
61
|
+
const result = await akmInit({
|
|
62
|
+
dir: args.dir ?? legacyDir,
|
|
63
|
+
setDefault: getHyphenatedBoolean(args, "set-default"),
|
|
64
|
+
});
|
|
57
65
|
output("init", result);
|
|
58
66
|
},
|
|
59
67
|
});
|
|
@@ -71,6 +79,11 @@ export const indexCommand = defineCommand({
|
|
|
71
79
|
description: "When combined with --clean, report stale entries without deleting them.",
|
|
72
80
|
default: false,
|
|
73
81
|
},
|
|
82
|
+
background: {
|
|
83
|
+
type: "boolean",
|
|
84
|
+
description: "Run as a background process (suppresses interactive output, manages PID file).",
|
|
85
|
+
default: false,
|
|
86
|
+
},
|
|
74
87
|
},
|
|
75
88
|
async run({ args }) {
|
|
76
89
|
await runWithJsonErrors(async () => {
|
|
@@ -80,6 +93,7 @@ export const indexCommand = defineCommand({
|
|
|
80
93
|
if (getHyphenatedBoolean(args, "re-enrich") || parseFlagValue(process.argv, "--re-enrich") !== undefined) {
|
|
81
94
|
throw new UsageError("`akm index --re-enrich` has been removed. Re-enrichment of index-time LLM passes is not exposed in this slice.");
|
|
82
95
|
}
|
|
96
|
+
const isBackground = args.background === true;
|
|
83
97
|
const outputMode = getOutputMode();
|
|
84
98
|
const controller = new AbortController();
|
|
85
99
|
const abort = () => controller.abort(new Error("index interrupted"));
|
|
@@ -88,7 +102,7 @@ export const indexCommand = defineCommand({
|
|
|
88
102
|
const indexLogFile = path.join(getCacheDir(), "logs", "index", `${new Date().toISOString().replace(/[:.]/g, "-")}.log`);
|
|
89
103
|
setLogFile(indexLogFile);
|
|
90
104
|
const verbose = isVerbose();
|
|
91
|
-
const spin = !verbose && outputMode.format === "text" ? p.spinner() : null;
|
|
105
|
+
const spin = !verbose && !isBackground && outputMode.format === "text" ? p.spinner() : null;
|
|
92
106
|
if (spin) {
|
|
93
107
|
spin.start(`Building search index${args.full ? " (full rebuild)" : ""}...`);
|
|
94
108
|
}
|
|
@@ -114,7 +128,9 @@ export const indexCommand = defineCommand({
|
|
|
114
128
|
if (spin) {
|
|
115
129
|
spin.stop(`Indexed ${result.totalEntries} assets.`);
|
|
116
130
|
}
|
|
117
|
-
|
|
131
|
+
if (!isBackground) {
|
|
132
|
+
output("index", result);
|
|
133
|
+
}
|
|
118
134
|
}
|
|
119
135
|
catch (error) {
|
|
120
136
|
if (spin) {
|
|
@@ -137,35 +153,6 @@ export const infoCommand = defineJsonCommand({
|
|
|
137
153
|
output("info", result);
|
|
138
154
|
},
|
|
139
155
|
});
|
|
140
|
-
// MVP DB administration. Currently only `akm db backups`; restore is manual —
|
|
141
|
-
// stop akm and run `scripts/migrations/restore-data-dir.sh <backup>`.
|
|
142
|
-
// Single source of truth: the routing set is derived from the subCommands keys
|
|
143
|
-
// (M10) so adding a subcommand can never silently desync from `hasSubcommand`.
|
|
144
|
-
const dbSubCommands = {
|
|
145
|
-
backups: defineJsonCommand({
|
|
146
|
-
meta: {
|
|
147
|
-
name: "backups",
|
|
148
|
-
description: "List pre-upgrade snapshots of the data directory (newest first). Backups are created automatically before destructive DB version upgrades unless AKM_DB_BACKUP=0.",
|
|
149
|
-
},
|
|
150
|
-
run() {
|
|
151
|
-
output("db-backups", akmDbBackups());
|
|
152
|
-
},
|
|
153
|
-
}),
|
|
154
|
-
};
|
|
155
|
-
const DB_SUBCOMMAND_SET = new Set(Object.keys(dbSubCommands));
|
|
156
|
-
export const dbCommand = defineJsonCommand({
|
|
157
|
-
meta: {
|
|
158
|
-
name: "db",
|
|
159
|
-
description: "Inspect the AKM SQLite data directory. Currently exposes `backups`; to restore from a snapshot, stop akm and run scripts/migrations/restore-data-dir.sh against the chosen backup.",
|
|
160
|
-
},
|
|
161
|
-
subCommands: dbSubCommands,
|
|
162
|
-
run({ args }) {
|
|
163
|
-
if (hasSubcommand(args, DB_SUBCOMMAND_SET))
|
|
164
|
-
return;
|
|
165
|
-
// Default action: list backups.
|
|
166
|
-
output("db-backups", akmDbBackups());
|
|
167
|
-
},
|
|
168
|
-
});
|
|
169
156
|
export const importKnowledgeCommand = defineJsonCommand({
|
|
170
157
|
meta: {
|
|
171
158
|
name: "import",
|
|
@@ -198,7 +185,8 @@ export const importKnowledgeCommand = defineJsonCommand({
|
|
|
198
185
|
async run({ args }) {
|
|
199
186
|
// `--name` is a flat name; subdirectory placement is `--path`'s job.
|
|
200
187
|
assertFlatAssetName(args.name);
|
|
201
|
-
const
|
|
188
|
+
const stashDir = resolveWriteTarget(loadConfig(), args.target).source.path;
|
|
189
|
+
const { content, preferredName } = await readKnowledgeInput(args.source, { stashDir });
|
|
202
190
|
const result = await writeMarkdownAsset({
|
|
203
191
|
type: "knowledge",
|
|
204
192
|
content,
|
|
@@ -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 {
|
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
* with the family.
|
|
14
14
|
*/
|
|
15
15
|
import { defineCommand } from "citty";
|
|
16
|
-
import {
|
|
17
|
-
import { defineJsonCommand, output, runWithJsonErrors } from "../../cli/shared.js";
|
|
16
|
+
import { parsePositiveIntFlag } from "../../cli/parse-args.js";
|
|
17
|
+
import { defineGroupCommand, defineJsonCommand, output, runWithJsonErrors } from "../../cli/shared.js";
|
|
18
18
|
import { getHyphenatedArg } from "../../output/context.js";
|
|
19
19
|
import { detectServerDefault, registerDefaultTasks } from "./default-tasks.js";
|
|
20
20
|
import { akmTasksAdd, akmTasksDoctor, akmTasksHistory, akmTasksList, akmTasksRemove, akmTasksRun, akmTasksSetEnabled, akmTasksShow, akmTasksSync, parseTaskRef, } from "./tasks.js";
|
|
@@ -176,35 +176,27 @@ const tasksDoctorCommand = defineJsonCommand({
|
|
|
176
176
|
output("tasks-doctor", result);
|
|
177
177
|
},
|
|
178
178
|
});
|
|
179
|
-
|
|
180
|
-
// (M10) so adding a subcommand can never silently desync from `hasSubcommand`.
|
|
181
|
-
const tasksSubCommands = {
|
|
182
|
-
add: tasksAddCommand,
|
|
183
|
-
init: tasksInitCommand,
|
|
184
|
-
list: tasksListCommand,
|
|
185
|
-
show: tasksShowCommand,
|
|
186
|
-
remove: tasksRemoveCommand,
|
|
187
|
-
enable: tasksEnableCommand,
|
|
188
|
-
disable: tasksDisableCommand,
|
|
189
|
-
run: tasksRunCommand,
|
|
190
|
-
history: tasksHistoryCommand,
|
|
191
|
-
sync: tasksSyncCommand,
|
|
192
|
-
doctor: tasksDoctorCommand,
|
|
193
|
-
};
|
|
194
|
-
const TASKS_SUBCOMMAND_SET = new Set(Object.keys(tasksSubCommands));
|
|
195
|
-
export const tasksCommand = defineCommand({
|
|
179
|
+
export const tasksCommand = defineGroupCommand({
|
|
196
180
|
meta: {
|
|
197
181
|
name: "tasks",
|
|
198
182
|
alias: "task",
|
|
199
183
|
description: "Schedule workflows or prompts via the OS-native scheduler (cron / launchd / schtasks)",
|
|
200
184
|
},
|
|
201
|
-
subCommands:
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
185
|
+
subCommands: {
|
|
186
|
+
add: tasksAddCommand,
|
|
187
|
+
init: tasksInitCommand,
|
|
188
|
+
list: tasksListCommand,
|
|
189
|
+
show: tasksShowCommand,
|
|
190
|
+
remove: tasksRemoveCommand,
|
|
191
|
+
enable: tasksEnableCommand,
|
|
192
|
+
disable: tasksDisableCommand,
|
|
193
|
+
run: tasksRunCommand,
|
|
194
|
+
history: tasksHistoryCommand,
|
|
195
|
+
sync: tasksSyncCommand,
|
|
196
|
+
doctor: tasksDoctorCommand,
|
|
197
|
+
},
|
|
198
|
+
async defaultRun() {
|
|
199
|
+
const result = await akmTasksList();
|
|
200
|
+
output("tasks-list", result);
|
|
209
201
|
},
|
|
210
202
|
});
|
|
@@ -20,7 +20,7 @@ import { listAgentProfileNames } from "../../integrations/agent/index.js";
|
|
|
20
20
|
import { resolveAssetPath } from "../../sources/resolve.js";
|
|
21
21
|
import { backendNameForPlatform, selectBackend } from "../../tasks/backends/index.js";
|
|
22
22
|
import { parseTaskDocument } from "../../tasks/parser.js";
|
|
23
|
-
import { resolveAkmInvocation } from "../../tasks/
|
|
23
|
+
import { resolveAkmInvocation } from "../../tasks/resolve-akm-bin.js";
|
|
24
24
|
import { exitCodeForStatus, readTaskHistory, runTask } from "../../tasks/runner.js";
|
|
25
25
|
import { parseSchedule, SCHEDULE_SUPPORTED_SUBSET_HINT, translateToCron } from "../../tasks/schedule.js";
|
|
26
26
|
import { validateTaskDocument } from "../../tasks/validator.js";
|
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
* `process.exit(1)` after the wrapper when findings exist.
|
|
12
12
|
*/
|
|
13
13
|
import { defineCommand } from "citty";
|
|
14
|
-
import { getStringArg,
|
|
15
|
-
import { defineJsonCommand, output, runWithJsonErrors } from "../cli/shared.js";
|
|
16
|
-
import {
|
|
14
|
+
import { getStringArg, parsePositiveIntFlag } from "../cli/parse-args.js";
|
|
15
|
+
import { defineGroupCommand, defineJsonCommand, output, runWithJsonErrors } from "../cli/shared.js";
|
|
16
|
+
import { resolveStashDir } from "../core/common.js";
|
|
17
17
|
import { loadConfig, resolveConfiguredSources } from "../core/config/config.js";
|
|
18
18
|
import { ConfigError, UsageError } from "../core/errors.js";
|
|
19
19
|
import { getHyphenatedArg, getHyphenatedBoolean } from "../output/context.js";
|
|
@@ -168,13 +168,6 @@ const wikiStashCommand = defineJsonCommand({
|
|
|
168
168
|
},
|
|
169
169
|
async run({ args }) {
|
|
170
170
|
const { stashRaw } = await import("../wiki/wiki.js");
|
|
171
|
-
const { content, preferredName } = await (async () => {
|
|
172
|
-
if (!isHttpUrl(args.source))
|
|
173
|
-
return readKnowledgeInput(args.source);
|
|
174
|
-
const { fetchWebsiteMarkdownSnapshot } = await import("../sources/website-ingest.js");
|
|
175
|
-
const snapshot = await fetchWebsiteMarkdownSnapshot(args.source);
|
|
176
|
-
return { content: snapshot.content, preferredName: args.as ?? snapshot.preferredName };
|
|
177
|
-
})();
|
|
178
171
|
let stashDir;
|
|
179
172
|
if (args.target) {
|
|
180
173
|
// Resolve the named source to its filesystem path.
|
|
@@ -193,6 +186,7 @@ const wikiStashCommand = defineJsonCommand({
|
|
|
193
186
|
else {
|
|
194
187
|
stashDir = resolveStashDir();
|
|
195
188
|
}
|
|
189
|
+
const { content, preferredName } = await readKnowledgeInput(args.source, { stashDir });
|
|
196
190
|
const result = stashRaw({
|
|
197
191
|
stashDir,
|
|
198
192
|
wikiName: args.name,
|
|
@@ -274,34 +268,26 @@ const wikiIngestCommand = defineJsonCommand({
|
|
|
274
268
|
});
|
|
275
269
|
},
|
|
276
270
|
});
|
|
277
|
-
|
|
278
|
-
// (M10) so adding a subcommand can never silently desync from `hasSubcommand`.
|
|
279
|
-
const wikiSubCommands = {
|
|
280
|
-
create: wikiCreateCommand,
|
|
281
|
-
register: wikiRegisterCommand,
|
|
282
|
-
list: wikiListCommand,
|
|
283
|
-
show: wikiShowCommand,
|
|
284
|
-
remove: wikiRemoveCommand,
|
|
285
|
-
pages: wikiPagesCommand,
|
|
286
|
-
search: wikiSearchCommand,
|
|
287
|
-
stash: wikiStashCommand,
|
|
288
|
-
lint: wikiLintCommand,
|
|
289
|
-
ingest: wikiIngestCommand,
|
|
290
|
-
};
|
|
291
|
-
const WIKI_SUBCOMMAND_SET = new Set(Object.keys(wikiSubCommands));
|
|
292
|
-
export const wikiCommand = defineCommand({
|
|
271
|
+
export const wikiCommand = defineGroupCommand({
|
|
293
272
|
meta: {
|
|
294
273
|
name: "wiki",
|
|
295
274
|
description: "Manage multiple markdown wikis (Karpathy-style). akm surfaces (lifecycle, raw/, lint, index); the agent writes pages.",
|
|
296
275
|
},
|
|
297
|
-
subCommands:
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
276
|
+
subCommands: {
|
|
277
|
+
create: wikiCreateCommand,
|
|
278
|
+
register: wikiRegisterCommand,
|
|
279
|
+
list: wikiListCommand,
|
|
280
|
+
show: wikiShowCommand,
|
|
281
|
+
remove: wikiRemoveCommand,
|
|
282
|
+
pages: wikiPagesCommand,
|
|
283
|
+
search: wikiSearchCommand,
|
|
284
|
+
stash: wikiStashCommand,
|
|
285
|
+
lint: wikiLintCommand,
|
|
286
|
+
ingest: wikiIngestCommand,
|
|
287
|
+
},
|
|
288
|
+
async defaultRun() {
|
|
289
|
+
// Default action: list wikis
|
|
290
|
+
const { listWikis } = await import("../wiki/wiki.js");
|
|
291
|
+
output("wiki-list", { wikis: listWikis(resolveStashDir()) });
|
|
306
292
|
},
|
|
307
293
|
});
|