akm-cli 0.9.0-beta.39 → 0.9.0-beta.40
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
CHANGED
|
@@ -6,6 +6,27 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- **BEHAVIOR CHANGE — `akm init --dir <path>` no longer silently repoints your
|
|
12
|
+
default stash.** Previously, `akm init --dir X` unconditionally wrote
|
|
13
|
+
`stashDir: X` to `config.json` whenever `X` differed from the configured
|
|
14
|
+
default — so initializing a throwaway or secondary stash (e.g.
|
|
15
|
+
`akm init --dir /tmp/scratch`) would hijack the user's real default stash
|
|
16
|
+
pointer (the footgun documented in `memory:akm-init-persists-stashdir-warning`).
|
|
17
|
+
Now `init` persists `stashDir` to config **only** when one of the following
|
|
18
|
+
holds: (a) **no `--dir`** was provided (the default `~/akm` setup flow —
|
|
19
|
+
unchanged), (b) `--dir` was provided and **no `stashDir` exists in config yet**
|
|
20
|
+
(first-time bootstrap), or (c) `--dir` was provided **with the new
|
|
21
|
+
`--set-default` flag** (explicit opt-in). Otherwise `init` still scaffolds and
|
|
22
|
+
backfills the target dir exactly as before, but **leaves your default stash
|
|
23
|
+
pointer untouched** and prints:
|
|
24
|
+
`Your default stash is unchanged (<existing>). Re-run with --set-default to make <dir> the default.`
|
|
25
|
+
The `InitResponse` JSON gains `defaultStashUpdated: boolean` and an optional
|
|
26
|
+
`previousStashDir`. To make a `--dir` target your default, pass
|
|
27
|
+
`akm init --dir <path> --set-default`. (`akm setup` is unaffected — it remains
|
|
28
|
+
the explicit configuration flow and always sets the default.)
|
|
29
|
+
|
|
9
30
|
### Added
|
|
10
31
|
|
|
11
32
|
- **Per-type SOFT authoring conventions are now user-editable stash facts.** A
|
|
@@ -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 });
|
|
@@ -82,11 +84,33 @@ export async function akmInit(options) {
|
|
|
82
84
|
// helpers are absent-only: they never overwrite a file a user has edited.
|
|
83
85
|
copyStashSkeleton(stashDir);
|
|
84
86
|
scaffoldStashMeta(stashDir);
|
|
85
|
-
// Persist stashDir in config.json
|
|
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.
|
|
86
98
|
const configPath = getConfigPath();
|
|
87
99
|
const existing = loadUserConfig();
|
|
88
|
-
|
|
89
|
-
|
|
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;
|
|
90
114
|
}
|
|
91
115
|
// Ensure ripgrep is available (install to cache/bin if needed)
|
|
92
116
|
let ripgrep;
|
|
@@ -98,7 +122,7 @@ export async function akmInit(options) {
|
|
|
98
122
|
catch {
|
|
99
123
|
// Non-fatal: ripgrep is optional, search works without it
|
|
100
124
|
}
|
|
101
|
-
return { stashDir, created, configPath, ripgrep };
|
|
125
|
+
return { stashDir, created, configPath, defaultStashUpdated, previousStashDir, ripgrep };
|
|
102
126
|
}
|
|
103
127
|
/** Initialise `dir` as a git repository if it is not already one. */
|
|
104
128
|
function ensureGitRepo(dir) {
|
|
@@ -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
|
});
|
|
@@ -1035,8 +1035,14 @@ export function formatCuratePlain(r, detail) {
|
|
|
1035
1035
|
}
|
|
1036
1036
|
export function formatInitPlain(r) {
|
|
1037
1037
|
let out = `Stash initialized at ${r.stashDir ?? "unknown"}`;
|
|
1038
|
-
|
|
1038
|
+
// When --dir scaffolded a secondary stash but the default was deliberately
|
|
1039
|
+
// left untouched, tell the user instead of silently repointing their default.
|
|
1040
|
+
if (r.defaultStashUpdated === false && typeof r.previousStashDir === "string" && r.previousStashDir) {
|
|
1041
|
+
out += `\nYour default stash is unchanged (${r.previousStashDir}). Re-run with --set-default to make ${r.stashDir} the default.`;
|
|
1042
|
+
}
|
|
1043
|
+
else if (r.configPath) {
|
|
1039
1044
|
out += `\nConfig saved to ${r.configPath}`;
|
|
1045
|
+
}
|
|
1040
1046
|
return out;
|
|
1041
1047
|
}
|
|
1042
1048
|
export function formatIndexPlain(r) {
|
package/dist/setup/setup.js
CHANGED
|
@@ -1791,7 +1791,7 @@ export async function runSetupWizard(opts) {
|
|
|
1791
1791
|
// Bootstrap directory structure before any prompts so the stash exists
|
|
1792
1792
|
// even if the wizard is interrupted after this point.
|
|
1793
1793
|
if (!opts?.noInit) {
|
|
1794
|
-
await akmInit({ dir: resolvedStashDir });
|
|
1794
|
+
await akmInit({ dir: resolvedStashDir, setDefault: true });
|
|
1795
1795
|
}
|
|
1796
1796
|
// Quick connectivity check — skip network-dependent steps when offline
|
|
1797
1797
|
const online = await isOnline();
|
|
@@ -1986,7 +1986,7 @@ export async function runSetupWithDefaults(opts) {
|
|
|
1986
1986
|
// Bootstrap directory structure first
|
|
1987
1987
|
let initResult;
|
|
1988
1988
|
if (!opts.noInit) {
|
|
1989
|
-
initResult = await akmInit({ dir: stashDir });
|
|
1989
|
+
initResult = await akmInit({ dir: stashDir, setDefault: true });
|
|
1990
1990
|
}
|
|
1991
1991
|
// Run steps in non-interactive mode (applies defaults, skips prompts)
|
|
1992
1992
|
const ctx = createSetupContext(current, { nonInteractive: true });
|
|
@@ -2283,7 +2283,7 @@ export async function runSetupFromConfig(opts) {
|
|
|
2283
2283
|
// Bootstrap directory structure
|
|
2284
2284
|
let initResult;
|
|
2285
2285
|
if (!opts.noInit) {
|
|
2286
|
-
initResult = await akmInit({ dir: stashDir });
|
|
2286
|
+
initResult = await akmInit({ dir: stashDir, setDefault: true });
|
|
2287
2287
|
}
|
|
2288
2288
|
// Optional probe
|
|
2289
2289
|
const mergedLlm = getDefaultLlmConfig(merged);
|
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.40",
|
|
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": [
|