rigjs 4.0.2 → 4.0.4
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/.claude/skills/rig-wiki/SKILL.md +13 -1
- package/RIG_WIKI_SKILL.md +13 -1
- package/built/index.js +224 -192
- package/lib/wiki/README.md +6 -6
- package/lib/wiki/config.ts +115 -36
- package/lib/wiki/index.ts +2 -2
- package/lib/wiki/ingest.ts +1 -1
- package/lib/wiki/init.ts +18 -1
- package/lib/wiki/paths.ts +7 -2
- package/lib/wiki/register.ts +65 -55
- package/lib/wiki/scan.ts +0 -0
- package/lib/wiki/unregister.ts +19 -7
- package/package.json +4 -2
package/lib/wiki/README.md
CHANGED
|
@@ -13,7 +13,7 @@ Convention: **one file per subcommand**, plus a small set of shared infra files
|
|
|
13
13
|
| `index.ts` | Commander wiring. Builds the `rig wiki` subtree and attaches every action. Imported once from `lib/rig/index.ts`. |
|
|
14
14
|
| `paths.ts` | Centralized filesystem paths (`~/.rig/`, launchd plist, Claude skills dir). Override with `RIG_HOME`. Also exports the launchd label. |
|
|
15
15
|
| `platform.ts` | `requireMacOS()` — hard-exits with code 32 on non-Darwin platforms. v1 is macOS-only by decision; see roadmap P5. |
|
|
16
|
-
| `config.ts` |
|
|
16
|
+
| `config.ts` | YAML read/write for `~/.rig/config.yml` (`RigConfig`, rig-global prefs) and `~/.rig/wikis.yml` (`Registry`, discovery-only path list). Per-vault settings live at `<vault>/.rig/config.yml` (`VaultConfig`). `loadWikiConfig()` composes the registry + each vault's config into the consumer-facing `WikiEntry[]`. `resolveWiki()` picks the target wiki for a command (flag → CWD walk → undefined). |
|
|
17
17
|
| `db.ts` | Lazy-loaded `better-sqlite3` singleton. WAL mode. Idempotent migrations on every open. Exposes `getDb()`, `recordLastRun()`, `getLastRun()`. |
|
|
18
18
|
| `qmd.ts` | Detects `qmd` on PATH, wraps `qmd query --json` and `qmd embed`. All callers must handle `installed=false` gracefully — qmd is optional. |
|
|
19
19
|
|
|
@@ -23,9 +23,9 @@ Convention: **one file per subcommand**, plus a small set of shared infra files
|
|
|
23
23
|
|
|
24
24
|
| File | Subcommand | What it does |
|
|
25
25
|
|---|---|---|
|
|
26
|
-
| `init.ts` | `rig wiki init [path]` | Bootstraps a fresh wiki dir: `purpose.md` + `schema.md` from templates, empty `index.md` / `overview.md` / `log.md` / `reviews.md`, `raw/` + five `wiki/<sub>/` dirs. Idempotent — never overwrites existing files. Does **not** register. |
|
|
27
|
-
| `register.ts` | `rig wiki register [path]` |
|
|
28
|
-
| `unregister.ts` | `rig wiki unregister <nameOrPath>` |
|
|
26
|
+
| `init.ts` | `rig wiki init [path]` | Bootstraps a fresh wiki dir: `purpose.md` + `schema.md` from templates, empty `index.md` / `overview.md` / `log.md` / `reviews.md`, `raw/` + five `wiki/<sub>/` dirs, and seeds `<vault>/.rig/config.yml` with default include / exclude / schedule. Idempotent — never overwrites existing files. Does **not** register. |
|
|
27
|
+
| `register.ts` | `rig wiki register [path]` | Ensures `<vault>/.rig/config.yml` exists (seeding defaults if absent), applies `--as <slug>` to its `name`, then appends the vault's absolute path to `~/.rig/wikis.yml`. Auto-detects the vault by walking up from CWD looking for `purpose.md`. |
|
|
28
|
+
| `unregister.ts` | `rig wiki unregister <nameOrPath>` | Drops the vault path from `~/.rig/wikis.yml`. `<vault>/.rig/config.yml` and disk content are untouched. |
|
|
29
29
|
| `list.ts` | `rig wiki list` | Prints a table: name, path, page count, last scan / ingest / lint. Banner row shows detected agent CLI, qmd status. |
|
|
30
30
|
| `scan.ts` | `rig wiki scan [path]` | Walks `include` globs + `raw/`, sha256-compares against the `source_sha` table in `state.db`. Emits NEW / MODIFIED / DELETED / RAW DRIFT report. Returns exit code 10 if any RAW DRIFT (raw/ files are immutable). No agent calls. |
|
|
31
31
|
| `fetch.ts` | `rig wiki fetch <url>` | (stub — P1) Agent-as-fetcher. Will WebFetch the URL via Claude adapter and write `raw/YYYY-MM-DD-<slug>.md` verbatim with frontmatter. **Never** summarizes. |
|
|
@@ -51,7 +51,7 @@ One adapter per agent CLI. Only Claude Code is implemented in v1; others are stu
|
|
|
51
51
|
| `codex.ts` | Stub. Detection works; `run()` throws. Open questions on codex's permission flags live in `doc/architecture/agents.md §4`. |
|
|
52
52
|
| `pi.ts` | Stub. Same shape as codex. Upstream CLI name not yet fixed. |
|
|
53
53
|
| `list.ts` | `rig wiki agent list` — iterates `adapters`, calls `detect()`, prints a table. Marks the default agent with `*`. |
|
|
54
|
-
| `use.ts` | `rig wiki agent use <name>` — writes `~/.rig/config.
|
|
54
|
+
| `use.ts` | `rig wiki agent use <name>` — writes `~/.rig/config.yml` `wiki.defaultAgent`. Rejects un-implemented adapters with exit code 20. |
|
|
55
55
|
|
|
56
56
|
---
|
|
57
57
|
|
|
@@ -66,7 +66,7 @@ One adapter per agent CLI. Only Claude Code is implemented in v1; others are stu
|
|
|
66
66
|
| `stop.ts` | `launchctl bootout` only. |
|
|
67
67
|
| `status.ts` | `launchctl print gui/<uid>/<label>`, parses `state=` and `pid=`. |
|
|
68
68
|
| `logs.ts` | Tails `~/.rig/logs/wiki-daemon.log` (with optional `-f`). |
|
|
69
|
-
| `runner.ts` | **The launchd entry.** `launchctl` invokes `node <rigjs>/built/index.js wiki daemon runner`. v1: heartbeat-only loop that logs every 10 min and reloads `
|
|
69
|
+
| `runner.ts` | **The launchd entry.** `launchctl` invokes `node <rigjs>/built/index.js wiki daemon runner`. v1: heartbeat-only loop that logs every 10 min and reloads `~/.rig/wikis.yml` + each vault's `.rig/config.yml`. P2 will add cron-based scan/lint scheduling and `auto-on-new` ingest rules. |
|
|
70
70
|
|
|
71
71
|
---
|
|
72
72
|
|
package/lib/wiki/config.ts
CHANGED
|
@@ -1,7 +1,20 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
-
import
|
|
4
|
-
import { paths } from './paths';
|
|
3
|
+
import yaml from 'js-yaml';
|
|
4
|
+
import { paths, vaultConfigPath } from './paths';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Two-layer config model.
|
|
8
|
+
*
|
|
9
|
+
* ~/.rig/config.yml — rig-global preferences (agent / qmd / logRotate).
|
|
10
|
+
* ~/.rig/wikis.yml — registry: list of absolute vault paths only.
|
|
11
|
+
* <vault>/.rig/config.yml — per-vault settings (name, include, exclude,
|
|
12
|
+
* schedule, ingestRules, optional scan root).
|
|
13
|
+
*
|
|
14
|
+
* A `WikiEntry` is the composed view returned to consumers (registry path
|
|
15
|
+
* + vault config + defaults). Nothing about a wiki's identity or scope lives
|
|
16
|
+
* outside the vault itself; the global registry is just a discovery list.
|
|
17
|
+
*/
|
|
5
18
|
|
|
6
19
|
export interface RigConfig {
|
|
7
20
|
wiki?: {
|
|
@@ -11,89 +24,155 @@ export interface RigConfig {
|
|
|
11
24
|
};
|
|
12
25
|
}
|
|
13
26
|
|
|
14
|
-
|
|
27
|
+
/** Per-vault config persisted at `<vault>/.rig/config.yml`. */
|
|
28
|
+
export interface VaultConfig {
|
|
15
29
|
name: string;
|
|
16
|
-
|
|
17
|
-
|
|
30
|
+
/** Scan root relative to the vault. Default: `..` (vault's parent dir). */
|
|
31
|
+
root?: string;
|
|
18
32
|
include?: string[];
|
|
19
33
|
exclude?: string[];
|
|
20
34
|
schedule?: { scan?: string; lint?: string; ingest?: string | null };
|
|
21
35
|
ingestRules?: { match: string; mode: 'auto-on-new' | 'propose-only' }[];
|
|
22
36
|
}
|
|
23
37
|
|
|
38
|
+
/** Global registry persisted at `~/.rig/wikis.yml`. */
|
|
39
|
+
export interface Registry {
|
|
40
|
+
wikis: string[]; // absolute vault paths
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Composed view used by all wiki commands. */
|
|
44
|
+
export interface WikiEntry {
|
|
45
|
+
name: string;
|
|
46
|
+
/** Absolute path to the vault dir. */
|
|
47
|
+
path: string;
|
|
48
|
+
/** Absolute path to the scan root (resolved from VaultConfig.root). */
|
|
49
|
+
root: string;
|
|
50
|
+
include: string[];
|
|
51
|
+
exclude: string[];
|
|
52
|
+
schedule?: { scan?: string; lint?: string; ingest?: string | null };
|
|
53
|
+
ingestRules?: { match: string; mode: 'auto-on-new' | 'propose-only' }[];
|
|
54
|
+
}
|
|
55
|
+
|
|
24
56
|
export interface WikiConfig {
|
|
25
|
-
defaults?: {
|
|
26
|
-
schedule?: { scan?: string; lint?: string; ingest?: string | null };
|
|
27
|
-
};
|
|
28
57
|
wikis: WikiEntry[];
|
|
29
58
|
}
|
|
30
59
|
|
|
31
|
-
const
|
|
60
|
+
const DEFAULT_RIG_CONFIG: RigConfig = {
|
|
32
61
|
wiki: { defaultAgent: 'claude', qmd: { enabled: 'auto' }, logRotateMB: 50 },
|
|
33
62
|
};
|
|
34
63
|
|
|
35
|
-
const
|
|
36
|
-
defaults: { schedule: { scan: '0 */6 * * *', lint: '0 3 * * *', ingest: null } },
|
|
37
|
-
wikis: [],
|
|
38
|
-
};
|
|
64
|
+
const DEFAULT_SCHEDULE = { scan: '0 */6 * * *', lint: '0 3 * * *', ingest: null };
|
|
39
65
|
|
|
40
|
-
function ensureHomeDir() {
|
|
66
|
+
function ensureHomeDir(): void {
|
|
41
67
|
fs.mkdirSync(paths.home, { recursive: true });
|
|
42
68
|
fs.mkdirSync(paths.locks, { recursive: true });
|
|
43
69
|
fs.mkdirSync(paths.logs, { recursive: true });
|
|
44
70
|
fs.mkdirSync(paths.cache, { recursive: true });
|
|
45
71
|
}
|
|
46
72
|
|
|
47
|
-
function
|
|
73
|
+
function readYaml<T>(file: string, fallback: T): T {
|
|
48
74
|
if (!fs.existsSync(file)) return fallback;
|
|
49
75
|
try {
|
|
50
|
-
|
|
76
|
+
const parsed = yaml.load(fs.readFileSync(file, 'utf8'));
|
|
77
|
+
return (parsed ?? fallback) as T;
|
|
51
78
|
} catch (e: any) {
|
|
52
79
|
throw new Error(`failed to parse ${file}: ${e.message}`);
|
|
53
80
|
}
|
|
54
81
|
}
|
|
55
82
|
|
|
56
|
-
function
|
|
57
|
-
fs.
|
|
83
|
+
function writeYaml(file: string, data: unknown): void {
|
|
84
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
85
|
+
fs.writeFileSync(file, yaml.dump(data, { lineWidth: 100, noRefs: true }), 'utf8');
|
|
58
86
|
}
|
|
59
87
|
|
|
88
|
+
// ─────────────────────────── rig-global config ───────────────────────────
|
|
89
|
+
|
|
60
90
|
export function loadRigConfig(): RigConfig {
|
|
61
91
|
ensureHomeDir();
|
|
62
|
-
return { ...
|
|
92
|
+
return { ...DEFAULT_RIG_CONFIG, ...readYaml<RigConfig>(paths.config, DEFAULT_RIG_CONFIG) };
|
|
63
93
|
}
|
|
64
94
|
|
|
65
|
-
export function saveRigConfig(cfg: RigConfig) {
|
|
95
|
+
export function saveRigConfig(cfg: RigConfig): void {
|
|
66
96
|
ensureHomeDir();
|
|
67
|
-
|
|
97
|
+
writeYaml(paths.config, cfg);
|
|
68
98
|
}
|
|
69
99
|
|
|
70
|
-
|
|
100
|
+
// ─────────────────────────── registry (paths only) ───────────────────────
|
|
101
|
+
|
|
102
|
+
export function loadRegistry(): Registry {
|
|
71
103
|
ensureHomeDir();
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
return { ...DEFAULT_WIKI_CONFIG, ...cfg };
|
|
104
|
+
const reg = readYaml<Registry>(paths.registry, { wikis: [] });
|
|
105
|
+
if (!Array.isArray(reg.wikis)) reg.wikis = [];
|
|
106
|
+
return reg;
|
|
76
107
|
}
|
|
77
108
|
|
|
78
|
-
export function
|
|
109
|
+
export function saveRegistry(reg: Registry): void {
|
|
79
110
|
ensureHomeDir();
|
|
80
|
-
|
|
111
|
+
writeYaml(paths.registry, { wikis: reg.wikis });
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ─────────────────────────── per-vault config ────────────────────────────
|
|
115
|
+
|
|
116
|
+
export function loadVaultConfig(vaultDir: string): VaultConfig | null {
|
|
117
|
+
const file = vaultConfigPath(vaultDir);
|
|
118
|
+
if (!fs.existsSync(file)) return null;
|
|
119
|
+
return readYaml<VaultConfig | null>(file, null);
|
|
81
120
|
}
|
|
82
121
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
122
|
+
export function saveVaultConfig(vaultDir: string, cfg: VaultConfig): void {
|
|
123
|
+
writeYaml(vaultConfigPath(vaultDir), cfg);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// ───────────────────────────── composed view ─────────────────────────────
|
|
127
|
+
|
|
128
|
+
const HARDCODED_EXCLUDES = ['node_modules/**', '.git/**'];
|
|
129
|
+
|
|
130
|
+
function composeEntry(vaultPath: string): WikiEntry | null {
|
|
131
|
+
const vault = loadVaultConfig(vaultPath);
|
|
132
|
+
if (!vault) return null;
|
|
133
|
+
const rootRel = vault.root ?? '..';
|
|
134
|
+
const root = path.resolve(vaultPath, rootRel);
|
|
135
|
+
const vaultBasename = path.basename(vaultPath);
|
|
136
|
+
return {
|
|
137
|
+
name: vault.name,
|
|
138
|
+
path: vaultPath,
|
|
139
|
+
root,
|
|
140
|
+
include: vault.include ?? ['**/*.md'],
|
|
141
|
+
exclude: vault.exclude ?? [`${vaultBasename}/**`, ...HARDCODED_EXCLUDES],
|
|
142
|
+
schedule: vault.schedule ?? DEFAULT_SCHEDULE,
|
|
143
|
+
ingestRules: vault.ingestRules ?? [{ match: 'raw/**/*.md', mode: 'auto-on-new' }],
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Compose the list of registered wikis. Reads the registry and, for each
|
|
149
|
+
* recorded path, loads the vault's own `.rig/config.yml`. Paths whose vault
|
|
150
|
+
* config is missing or malformed are skipped silently (the user can re-run
|
|
151
|
+
* `rig wiki register <path>` to repair).
|
|
152
|
+
*/
|
|
153
|
+
export function loadWikiConfig(): WikiConfig {
|
|
154
|
+
const reg = loadRegistry();
|
|
155
|
+
const wikis: WikiEntry[] = [];
|
|
156
|
+
for (const p of reg.wikis) {
|
|
157
|
+
const entry = composeEntry(p);
|
|
158
|
+
if (entry) wikis.push(entry);
|
|
159
|
+
}
|
|
160
|
+
return { wikis };
|
|
86
161
|
}
|
|
87
162
|
|
|
88
163
|
/**
|
|
89
164
|
* Resolve target wiki for a command:
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
165
|
+
* 1. If `--wiki <name>` provided, look up by name.
|
|
166
|
+
* 2. Otherwise walk up from CWD; first match wins (by vault `path` or `root`).
|
|
167
|
+
* 3. Return undefined if nothing matches.
|
|
93
168
|
*/
|
|
94
169
|
export function resolveWiki(cfg: WikiConfig, wikiFlag?: string): WikiEntry | undefined {
|
|
95
170
|
if (wikiFlag) return cfg.wikis.find(w => w.name === wikiFlag);
|
|
96
171
|
const cwd = process.cwd();
|
|
97
|
-
return cfg.wikis.find(w =>
|
|
98
|
-
|
|
172
|
+
return cfg.wikis.find(w =>
|
|
173
|
+
cwd === w.path ||
|
|
174
|
+
cwd.startsWith(w.path + path.sep) ||
|
|
175
|
+
cwd === w.root ||
|
|
176
|
+
cwd.startsWith(w.root + path.sep),
|
|
177
|
+
);
|
|
99
178
|
}
|
package/lib/wiki/index.ts
CHANGED
|
@@ -27,13 +27,13 @@ export function registerWikiCommands(program: any): void {
|
|
|
27
27
|
.action(wikiInit);
|
|
28
28
|
|
|
29
29
|
wiki.command('register [path]')
|
|
30
|
-
.description('register a
|
|
30
|
+
.description('register a vault into ~/.rig/wikis.yml (settings live in <vault>/.rig/config.yml)')
|
|
31
31
|
.option('-n, --as <slug>', 'override the wiki name (`--name` would clash with commander)')
|
|
32
32
|
.option('-f, --force', 'overwrite an existing entry with the same name')
|
|
33
33
|
.action(wikiRegister);
|
|
34
34
|
|
|
35
35
|
wiki.command('unregister <nameOrPath>')
|
|
36
|
-
.description('remove a
|
|
36
|
+
.description('remove a vault from ~/.rig/wikis.yml (vault contents on disk untouched)')
|
|
37
37
|
.action(wikiUnregister);
|
|
38
38
|
|
|
39
39
|
wiki.command('list')
|
package/lib/wiki/ingest.ts
CHANGED
|
@@ -45,7 +45,7 @@ export default async function wikiIngest(source: string, opts: IngestOpts): Prom
|
|
|
45
45
|
print.error(`source not found: ${source}`);
|
|
46
46
|
process.exit(1);
|
|
47
47
|
}
|
|
48
|
-
const guard = guardPath(absSource, target.
|
|
48
|
+
const guard = guardPath(absSource, target.root || target.path);
|
|
49
49
|
if (!guard.ok) {
|
|
50
50
|
print.error('refusing to ingest from a hidden or gitignored path.');
|
|
51
51
|
// eslint-disable-next-line no-console
|
package/lib/wiki/init.ts
CHANGED
|
@@ -2,6 +2,7 @@ import fs from 'fs';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import print from '../print';
|
|
4
4
|
import { guardPath, refusalMessage } from './pathGuard';
|
|
5
|
+
import { saveVaultConfig, VaultConfig } from './config';
|
|
5
6
|
|
|
6
7
|
const PURPOSE_TMPL = `# Purpose
|
|
7
8
|
|
|
@@ -75,6 +76,15 @@ proposals/
|
|
|
75
76
|
*.swp
|
|
76
77
|
`;
|
|
77
78
|
|
|
79
|
+
const DEFAULT_VAULT_CONFIG = (vaultBasename: string): VaultConfig => ({
|
|
80
|
+
name: vaultBasename,
|
|
81
|
+
root: '..',
|
|
82
|
+
include: ['**/*.md'],
|
|
83
|
+
exclude: [`${vaultBasename}/**`, 'node_modules/**', '.git/**'],
|
|
84
|
+
schedule: { scan: '0 */6 * * *', lint: '0 3 * * *', ingest: null },
|
|
85
|
+
ingestRules: [{ match: 'raw/**/*.md', mode: 'auto-on-new' }],
|
|
86
|
+
});
|
|
87
|
+
|
|
78
88
|
export default function wikiInit(givenPath?: string): void {
|
|
79
89
|
if (!givenPath || !givenPath.trim()) {
|
|
80
90
|
print.error('rig wiki init requires a target subdirectory.');
|
|
@@ -109,8 +119,15 @@ export default function wikiInit(givenPath?: string): void {
|
|
|
109
119
|
writeIfMissing(path.join(d, '.gitkeep'), '');
|
|
110
120
|
}
|
|
111
121
|
|
|
122
|
+
// Seed `<vault>/.rig/config.yml` with sensible defaults. Idempotent: if the
|
|
123
|
+
// user has already authored one, leave it alone.
|
|
124
|
+
const vaultCfgFile = path.join(root, '.rig', 'config.yml');
|
|
125
|
+
if (!fs.existsSync(vaultCfgFile)) {
|
|
126
|
+
saveVaultConfig(root, DEFAULT_VAULT_CONFIG(path.basename(root)));
|
|
127
|
+
}
|
|
128
|
+
|
|
112
129
|
print.succeed(`wiki initialized at ${root}`);
|
|
113
|
-
print.info(`next: edit purpose.md + schema.md, then \`rig wiki register ${shortPath(root)}\``);
|
|
130
|
+
print.info(`next: edit purpose.md + schema.md (and .rig/config.yml if scope differs from defaults), then \`rig wiki register ${shortPath(root)}\``);
|
|
114
131
|
print.info('on a new device, after cloning, run `rig wiki rebuild` to refresh local caches.');
|
|
115
132
|
}
|
|
116
133
|
|
package/lib/wiki/paths.ts
CHANGED
|
@@ -5,8 +5,8 @@ export const RIG_HOME = process.env.RIG_HOME || path.join(os.homedir(), '.rig');
|
|
|
5
5
|
|
|
6
6
|
export const paths = {
|
|
7
7
|
home: RIG_HOME,
|
|
8
|
-
config: path.join(RIG_HOME, 'config.
|
|
9
|
-
|
|
8
|
+
config: path.join(RIG_HOME, 'config.yml'),
|
|
9
|
+
registry: path.join(RIG_HOME, 'wikis.yml'),
|
|
10
10
|
stateDb: path.join(RIG_HOME, 'state.db'),
|
|
11
11
|
locks: path.join(RIG_HOME, 'locks'),
|
|
12
12
|
logs: path.join(RIG_HOME, 'logs'),
|
|
@@ -24,6 +24,11 @@ export function wikiLogDir(wikiName: string) {
|
|
|
24
24
|
return path.join(paths.logs, 'wikis', wikiName);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
/** Per-vault config path: `<vault>/.rig/config.yml`. */
|
|
28
|
+
export function vaultConfigPath(vaultDir: string) {
|
|
29
|
+
return path.join(vaultDir, '.rig', 'config.yml');
|
|
30
|
+
}
|
|
31
|
+
|
|
27
32
|
export function wikiLockFile(wikiName: string) {
|
|
28
33
|
return path.join(paths.locks, `${wikiName}.lock`);
|
|
29
34
|
}
|
package/lib/wiki/register.ts
CHANGED
|
@@ -1,58 +1,82 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
-
import JSON5 from 'json5';
|
|
4
3
|
import print from '../print';
|
|
5
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
loadRegistry,
|
|
6
|
+
saveRegistry,
|
|
7
|
+
loadVaultConfig,
|
|
8
|
+
saveVaultConfig,
|
|
9
|
+
VaultConfig,
|
|
10
|
+
} from './config';
|
|
6
11
|
|
|
7
12
|
interface RegisterOpts {
|
|
8
13
|
as?: string;
|
|
9
14
|
force?: boolean;
|
|
10
15
|
}
|
|
11
16
|
|
|
17
|
+
/**
|
|
18
|
+
* `rig wiki register [path]`
|
|
19
|
+
*
|
|
20
|
+
* 1. Resolve the vault dir (explicit arg, or walk up from CWD looking for
|
|
21
|
+
* a directory that already has `purpose.md`).
|
|
22
|
+
* 2. Ensure `<vault>/.rig/config.yml` exists. Create it from defaults if
|
|
23
|
+
* missing; otherwise keep whatever the user authored.
|
|
24
|
+
* 3. Apply the optional `--as <slug>` override to the vault config's name.
|
|
25
|
+
* 4. Append the absolute vault path to `~/.rig/wikis.yml` (the
|
|
26
|
+
* discovery-only registry). No-op if already present.
|
|
27
|
+
*/
|
|
12
28
|
export default function wikiRegister(givenPath: string | undefined, opts: RegisterOpts): void {
|
|
13
|
-
const
|
|
14
|
-
if (!fs.existsSync(
|
|
15
|
-
print.error(`path does not exist: ${
|
|
29
|
+
const vaultPath = path.resolve(givenPath || detectVaultPath(process.cwd()) || process.cwd());
|
|
30
|
+
if (!fs.existsSync(vaultPath)) {
|
|
31
|
+
print.error(`path does not exist: ${vaultPath}`);
|
|
16
32
|
process.exit(1);
|
|
17
33
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
print.error('failed to derive a wiki name; pass --name <n>');
|
|
34
|
+
if (!fs.existsSync(path.join(vaultPath, 'purpose.md'))) {
|
|
35
|
+
print.error(`not a wiki vault (no purpose.md): ${vaultPath}`);
|
|
36
|
+
print.info(`run \`rig wiki init ${shortPath(vaultPath)}\` first.`);
|
|
22
37
|
process.exit(1);
|
|
23
38
|
}
|
|
24
39
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
if (
|
|
28
|
-
|
|
40
|
+
// Load or seed the per-vault config.
|
|
41
|
+
let vault = loadVaultConfig(vaultPath);
|
|
42
|
+
if (!vault) {
|
|
43
|
+
vault = defaultVaultConfig(path.basename(vaultPath));
|
|
44
|
+
}
|
|
45
|
+
const desiredName = (opts.as || vault.name || path.basename(vaultPath)).trim();
|
|
46
|
+
if (!desiredName) {
|
|
47
|
+
print.error('failed to derive a wiki name; pass --as <slug>');
|
|
29
48
|
process.exit(1);
|
|
30
49
|
}
|
|
31
50
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
51
|
+
// Name collision in the registry — surfaced by composing the registry.
|
|
52
|
+
const reg = loadRegistry();
|
|
53
|
+
for (const existingPath of reg.wikis) {
|
|
54
|
+
if (existingPath === vaultPath) continue;
|
|
55
|
+
const other = loadVaultConfig(existingPath);
|
|
56
|
+
if (other && other.name === desiredName) {
|
|
57
|
+
if (!opts.force) {
|
|
58
|
+
print.error(`wiki "${desiredName}" already registered at ${existingPath}; pass --force to overwrite`);
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
// --force: drop the old entry, the new one wins.
|
|
62
|
+
reg.wikis = reg.wikis.filter(p => p !== existingPath);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
41
65
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
saveWikiConfig(cfg);
|
|
66
|
+
vault.name = desiredName;
|
|
67
|
+
saveVaultConfig(vaultPath, vault);
|
|
45
68
|
|
|
46
|
-
|
|
47
|
-
|
|
69
|
+
if (!reg.wikis.includes(vaultPath)) reg.wikis.push(vaultPath);
|
|
70
|
+
saveRegistry(reg);
|
|
48
71
|
|
|
49
|
-
print.succeed(`registered wiki "${
|
|
72
|
+
print.succeed(`registered wiki "${desiredName}" -> ${vaultPath}`);
|
|
50
73
|
}
|
|
51
74
|
|
|
52
|
-
function
|
|
75
|
+
function detectVaultPath(start: string): string | undefined {
|
|
53
76
|
const candidates = ['harness/llm-wiki', 'wiki'];
|
|
54
77
|
let dir = start;
|
|
55
78
|
while (true) {
|
|
79
|
+
if (fs.existsSync(path.join(dir, 'purpose.md'))) return dir;
|
|
56
80
|
for (const c of candidates) {
|
|
57
81
|
const cand = path.join(dir, c);
|
|
58
82
|
if (fs.existsSync(path.join(cand, 'purpose.md'))) return cand;
|
|
@@ -63,32 +87,18 @@ function detectWikiPath(start: string): string | undefined {
|
|
|
63
87
|
}
|
|
64
88
|
}
|
|
65
89
|
|
|
66
|
-
function
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function detectName(project: string | undefined, wikiPath: string): string {
|
|
77
|
-
if (project) {
|
|
78
|
-
try {
|
|
79
|
-
const pkg = JSON.parse(fs.readFileSync(path.join(project, 'package.json'), 'utf8'));
|
|
80
|
-
if (pkg && typeof pkg.name === 'string') return pkg.name.replace(/^@.*\//, '');
|
|
81
|
-
} catch { /* fall through */ }
|
|
82
|
-
}
|
|
83
|
-
return path.basename(path.dirname(wikiPath));
|
|
90
|
+
function defaultVaultConfig(vaultBasename: string): VaultConfig {
|
|
91
|
+
return {
|
|
92
|
+
name: vaultBasename,
|
|
93
|
+
root: '..',
|
|
94
|
+
include: ['**/*.md'],
|
|
95
|
+
exclude: [`${vaultBasename}/**`, 'node_modules/**', '.git/**'],
|
|
96
|
+
schedule: { scan: '0 */6 * * *', lint: '0 3 * * *', ingest: null },
|
|
97
|
+
ingestRules: [{ match: 'raw/**/*.md', mode: 'auto-on-new' }],
|
|
98
|
+
};
|
|
84
99
|
}
|
|
85
100
|
|
|
86
|
-
function
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
if (fs.existsSync(file)) {
|
|
90
|
-
try { cfg = JSON5.parse(fs.readFileSync(file, 'utf8')); } catch { cfg = {}; }
|
|
91
|
-
}
|
|
92
|
-
cfg.wiki = { name, path: path.relative(project, wikiPath) };
|
|
93
|
-
fs.writeFileSync(file, JSON5.stringify(cfg, null, 2) + '\n', 'utf8');
|
|
101
|
+
function shortPath(p: string): string {
|
|
102
|
+
const home = process.env.HOME || '';
|
|
103
|
+
return home && p.startsWith(home) ? '~' + p.slice(home.length) : p;
|
|
94
104
|
}
|
package/lib/wiki/scan.ts
CHANGED
|
Binary file
|
package/lib/wiki/unregister.ts
CHANGED
|
@@ -1,16 +1,28 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
2
|
import print from '../print';
|
|
3
|
-
import {
|
|
3
|
+
import { loadRegistry, saveRegistry, loadVaultConfig } from './config';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* `rig wiki unregister <nameOrPath>` — drop a vault path from
|
|
7
|
+
* `~/.rig/wikis.yml`. The vault's own `<vault>/.rig/config.yml` (and the
|
|
8
|
+
* rest of its on-disk contents) is left untouched.
|
|
9
|
+
*/
|
|
5
10
|
export default function wikiUnregister(nameOrPath: string): void {
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const before =
|
|
9
|
-
|
|
10
|
-
|
|
11
|
+
const reg = loadRegistry();
|
|
12
|
+
const targetAbs = path.resolve(nameOrPath);
|
|
13
|
+
const before = reg.wikis.length;
|
|
14
|
+
|
|
15
|
+
reg.wikis = reg.wikis.filter(p => {
|
|
16
|
+
if (p === targetAbs) return false;
|
|
17
|
+
const v = loadVaultConfig(p);
|
|
18
|
+
if (v && v.name === nameOrPath) return false;
|
|
19
|
+
return true;
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
if (reg.wikis.length === before) {
|
|
11
23
|
print.error(`no registered wiki matches "${nameOrPath}"`);
|
|
12
24
|
process.exit(1);
|
|
13
25
|
}
|
|
14
|
-
|
|
26
|
+
saveRegistry(reg);
|
|
15
27
|
print.succeed(`unregistered "${nameOrPath}" (disk contents untouched)`);
|
|
16
28
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rigjs",
|
|
3
|
-
"version": "4.0.
|
|
4
|
-
"versionCode":
|
|
3
|
+
"version": "4.0.4",
|
|
4
|
+
"versionCode": 26052411,
|
|
5
5
|
"description": "A multi-repos dev tool based on yarn and git.Rigjs is intended to be the simplest way to develop,share and deliver codes between different developers or different projects.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"modular",
|
|
@@ -79,6 +79,7 @@
|
|
|
79
79
|
"dayjs": "^1.11.0",
|
|
80
80
|
"dotenv": "^16.0.3",
|
|
81
81
|
"inquirer": "7.3.3",
|
|
82
|
+
"js-yaml": "^4.1.1",
|
|
82
83
|
"json5": "2.1.3",
|
|
83
84
|
"ora": "^5.1.0",
|
|
84
85
|
"semver": "^7.3.7",
|
|
@@ -87,6 +88,7 @@
|
|
|
87
88
|
},
|
|
88
89
|
"devDependencies": {
|
|
89
90
|
"@types/jest": "^28.1.1",
|
|
91
|
+
"@types/js-yaml": "^4.0.9",
|
|
90
92
|
"@types/node": "^17.0.21",
|
|
91
93
|
"esbuild": "0.28.0",
|
|
92
94
|
"jest": "^27.5.1",
|