skillwiki 0.8.10-beta.2 → 0.8.10-beta.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/dist/cli.js
CHANGED
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
} from "./chunk-TPS5XD2J.js";
|
|
15
15
|
|
|
16
16
|
// src/cli.ts
|
|
17
|
-
import { join as
|
|
17
|
+
import { join as join47 } from "path";
|
|
18
18
|
import { Command as Command2 } from "commander";
|
|
19
19
|
|
|
20
20
|
// ../shared/src/exit-codes.ts
|
|
@@ -68,7 +68,8 @@ var ExitCode = {
|
|
|
68
68
|
USAGE: 46,
|
|
69
69
|
BODY_TRUNCATION_GUARD: 47,
|
|
70
70
|
SYNC_LOCK_HELD: 48,
|
|
71
|
-
LOG_APPEND_LOCK_HELD: 49
|
|
71
|
+
LOG_APPEND_LOCK_HELD: 49,
|
|
72
|
+
FLEET_MANIFEST_INVALID: 50
|
|
72
73
|
};
|
|
73
74
|
|
|
74
75
|
// ../shared/src/json-output.ts
|
|
@@ -178,6 +179,57 @@ var MetaSchema = z.object({
|
|
|
178
179
|
ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["provenance_projects"], message: "required when provenance != research" });
|
|
179
180
|
}
|
|
180
181
|
});
|
|
182
|
+
var hostId = z.string().regex(/^[a-z0-9][a-z0-9_-]*$/, "must be a lowercase host id");
|
|
183
|
+
var endpointName = z.string().min(1).regex(/^[A-Za-z0-9_.-]+$/, "must be a hostname-like token");
|
|
184
|
+
var ipAddress = z.string().min(1).regex(/^[0-9a-fA-F:.]+$/, "must be an IP address token");
|
|
185
|
+
var sshAlias = z.string().min(1).regex(/^[A-Za-z0-9_.@-]+$/, "must be an SSH alias token");
|
|
186
|
+
var sshUser = z.string().min(1).regex(/^[A-Za-z0-9_.-]+$/, "must be an SSH user token");
|
|
187
|
+
var FleetAccessProfileSchema = z.object({
|
|
188
|
+
status: z.enum(["local", "configured", "planned", "absent", "unknown"]),
|
|
189
|
+
ssh_aliases: z.array(sshAlias).optional(),
|
|
190
|
+
users: z.array(sshUser).optional(),
|
|
191
|
+
transports: z.array(z.enum(["local", "public-ip", "tailscale", "private-lan"])).min(1)
|
|
192
|
+
}).strict();
|
|
193
|
+
var FleetHostIdentitySchema = z.object({
|
|
194
|
+
hostnames: z.array(endpointName).min(1),
|
|
195
|
+
public_addresses: z.array(ipAddress).optional(),
|
|
196
|
+
private_addresses: z.array(ipAddress).optional(),
|
|
197
|
+
tailscale: z.object({
|
|
198
|
+
node_names: z.array(endpointName).optional(),
|
|
199
|
+
magicdns_names: z.array(endpointName).optional(),
|
|
200
|
+
addresses: z.array(ipAddress).optional()
|
|
201
|
+
}).strict().optional()
|
|
202
|
+
}).strict();
|
|
203
|
+
var FleetHostSchema = z.object({
|
|
204
|
+
class: z.enum(["dev-macos", "dev-linux", "prod-linux", "unknown"]),
|
|
205
|
+
role: z.enum(["leaf", "snapshotter"]),
|
|
206
|
+
writes_to: z.array(z.enum(["s3", "github"])).min(1),
|
|
207
|
+
protected: z.boolean().optional(),
|
|
208
|
+
identity: FleetHostIdentitySchema,
|
|
209
|
+
access: z.object({
|
|
210
|
+
from: z.record(hostId, FleetAccessProfileSchema).optional()
|
|
211
|
+
}).strict().optional()
|
|
212
|
+
}).strict();
|
|
213
|
+
var FleetManifestSchema = z.object({
|
|
214
|
+
"$schema": z.string().url().optional(),
|
|
215
|
+
schema_version: z.literal(1),
|
|
216
|
+
vault_remote: z.string().min(1),
|
|
217
|
+
s3_remote: z.string().min(1).optional(),
|
|
218
|
+
hosts: z.record(hostId, FleetHostSchema)
|
|
219
|
+
}).strict().superRefine((v, ctx) => {
|
|
220
|
+
const entries = Object.entries(v.hosts);
|
|
221
|
+
if (entries.length === 0) {
|
|
222
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["hosts"], message: "must contain at least one host" });
|
|
223
|
+
}
|
|
224
|
+
const snapshotters = entries.filter(([, host]) => host.role === "snapshotter").map(([id]) => id);
|
|
225
|
+
if (snapshotters.length !== 1) {
|
|
226
|
+
ctx.addIssue({
|
|
227
|
+
code: z.ZodIssueCode.custom,
|
|
228
|
+
path: ["hosts"],
|
|
229
|
+
message: `must contain exactly one snapshotter host, found ${snapshotters.length}`
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
});
|
|
181
233
|
function detectSchema(fm) {
|
|
182
234
|
const COMPOUND_TYPES = /* @__PURE__ */ new Set(["lesson", "pattern", "antipattern", "gotcha"]);
|
|
183
235
|
if (typeof fm.type === "string" && COMPOUND_TYPES.has(fm.type) && "project" in fm) return { schema: "compound" };
|
|
@@ -793,6 +845,7 @@ import { dirname as dirname2 } from "path";
|
|
|
793
845
|
var CONFIG_KEYS = [
|
|
794
846
|
"WIKI_PATH",
|
|
795
847
|
"WIKI_LANG",
|
|
848
|
+
"SKILLWIKI_HOST_ID",
|
|
796
849
|
"AUTO_COMMIT",
|
|
797
850
|
"BACKUP_ENDPOINT",
|
|
798
851
|
"BACKUP_BUCKET",
|
|
@@ -3075,6 +3128,7 @@ function buildCliSurface() {
|
|
|
3075
3128
|
program2.command("observe").requiredOption("--text <text>").option("--kind <kind>").option("--project <slug>").option("--wiki <name>");
|
|
3076
3129
|
program2.command("session-brief").option("--project <slug>").option("--write").option("--wiki <name>");
|
|
3077
3130
|
program2.command("ingest").requiredOption("--vault <path>").requiredOption("--type <type>").requiredOption("--title <title>").option("--tags <csv>").option("--provenance <provenance>").option("--dry-run");
|
|
3131
|
+
program2.command("fleet");
|
|
3078
3132
|
const graphCmd = program2.commands.find((c) => c.name() === "graph");
|
|
3079
3133
|
graphCmd.command("build").option("--out <path>").option("--wiki <name>");
|
|
3080
3134
|
const canvasCmd2 = program2.commands.find((c) => c.name() === "canvas");
|
|
@@ -3098,6 +3152,9 @@ function buildCliSurface() {
|
|
|
3098
3152
|
const backupCmd2 = program2.commands.find((c) => c.name() === "backup");
|
|
3099
3153
|
backupCmd2.command("sync").option("--dry-run").option("--bucket <name>").option("--endpoint <url>").option("--region <region>").option("--prune").option("--wiki <name>");
|
|
3100
3154
|
backupCmd2.command("restore").option("--bucket <name>").option("--endpoint <url>").option("--region <region>").option("--target <dir>").option("--wiki <name>");
|
|
3155
|
+
const fleetCmd2 = program2.commands.find((c) => c.name() === "fleet");
|
|
3156
|
+
fleetCmd2.command("validate");
|
|
3157
|
+
fleetCmd2.command("context").option("--file <path>").option("--host-id <id>");
|
|
3101
3158
|
const surface = /* @__PURE__ */ new Map();
|
|
3102
3159
|
const rootFlags = new Set(program2.options.map((o) => o.long ?? o.short).filter((f) => f != null));
|
|
3103
3160
|
function walk2(cmd, prefix, parentFlags) {
|
|
@@ -9406,15 +9463,245 @@ async function loadOrBuildGraph(vault) {
|
|
|
9406
9463
|
}
|
|
9407
9464
|
}
|
|
9408
9465
|
|
|
9466
|
+
// src/commands/fleet.ts
|
|
9467
|
+
import { readFile as readFile29 } from "fs/promises";
|
|
9468
|
+
import { hostname as nodeHostname, userInfo } from "os";
|
|
9469
|
+
import { join as join45 } from "path";
|
|
9470
|
+
import yaml3 from "js-yaml";
|
|
9471
|
+
var FLEET_REL_PATH = join45("projects", "llm-wiki", "architecture", "fleet.yaml");
|
|
9472
|
+
async function runFleetValidate(input) {
|
|
9473
|
+
const loaded = await loadFleetManifest(input.file);
|
|
9474
|
+
if (!loaded.ok) {
|
|
9475
|
+
if (loaded.error === "FILE_NOT_FOUND") {
|
|
9476
|
+
return { exitCode: ExitCode.FILE_NOT_FOUND, result: err("FILE_NOT_FOUND", { path: input.file }) };
|
|
9477
|
+
}
|
|
9478
|
+
const errors = fleetLoadErrors(loaded);
|
|
9479
|
+
return invalidFleet(errors);
|
|
9480
|
+
}
|
|
9481
|
+
const warnings = fleetWarnings(loaded.manifest);
|
|
9482
|
+
const snapshotter = findSnapshotter(loaded.manifest);
|
|
9483
|
+
return {
|
|
9484
|
+
exitCode: ExitCode.OK,
|
|
9485
|
+
result: ok({
|
|
9486
|
+
valid: true,
|
|
9487
|
+
errors: [],
|
|
9488
|
+
warnings,
|
|
9489
|
+
host_count: Object.keys(loaded.manifest.hosts).length,
|
|
9490
|
+
snapshotter,
|
|
9491
|
+
humanHint: `VALID fleet manifest (${Object.keys(loaded.manifest.hosts).length} hosts; snapshotter: ${snapshotter ?? "none"})`
|
|
9492
|
+
})
|
|
9493
|
+
};
|
|
9494
|
+
}
|
|
9495
|
+
async function runFleetContext(input) {
|
|
9496
|
+
const env = input.env ?? process.env;
|
|
9497
|
+
const home = input.home ?? env.HOME ?? "";
|
|
9498
|
+
const cwd = input.cwd ?? process.cwd();
|
|
9499
|
+
const osHostname = input.osHostname ?? safeEnvValue(env.HOSTNAME) ?? nodeHostname();
|
|
9500
|
+
const user = input.user ?? safeEnvValue(env.USER) ?? safeUserName();
|
|
9501
|
+
const vault = input.vault ?? safeEnvValue(env.WIKI_PATH);
|
|
9502
|
+
const file = input.file ?? (vault ? join45(vault, FLEET_REL_PATH) : void 0);
|
|
9503
|
+
const loaded = file ? await loadFleetManifest(file) : { ok: false, error: "FILE_NOT_FOUND" };
|
|
9504
|
+
if (!loaded.ok) {
|
|
9505
|
+
const markdown2 = formatUnknownContext({ osHostname, user, cwd, vault, reason: "fleet manifest unavailable or invalid" });
|
|
9506
|
+
return {
|
|
9507
|
+
exitCode: ExitCode.OK,
|
|
9508
|
+
result: ok({ manifest_loaded: false, markdown: markdown2, humanHint: markdown2 })
|
|
9509
|
+
};
|
|
9510
|
+
}
|
|
9511
|
+
const resolved = await resolveHostId({
|
|
9512
|
+
manifest: loaded.manifest,
|
|
9513
|
+
hostId: input.hostId,
|
|
9514
|
+
env,
|
|
9515
|
+
home,
|
|
9516
|
+
osHostname
|
|
9517
|
+
});
|
|
9518
|
+
if (!resolved.hostId || !loaded.manifest.hosts[resolved.hostId]) {
|
|
9519
|
+
const markdown2 = formatUnknownContext({ osHostname, user, cwd, vault, reason: "host identity is unresolved" });
|
|
9520
|
+
return {
|
|
9521
|
+
exitCode: ExitCode.OK,
|
|
9522
|
+
result: ok({ manifest_loaded: true, markdown: markdown2, humanHint: markdown2 })
|
|
9523
|
+
};
|
|
9524
|
+
}
|
|
9525
|
+
const markdown = formatKnownContext({
|
|
9526
|
+
manifest: loaded.manifest,
|
|
9527
|
+
hostId: resolved.hostId,
|
|
9528
|
+
source: resolved.source,
|
|
9529
|
+
osHostname,
|
|
9530
|
+
user,
|
|
9531
|
+
cwd,
|
|
9532
|
+
vault
|
|
9533
|
+
});
|
|
9534
|
+
return {
|
|
9535
|
+
exitCode: ExitCode.OK,
|
|
9536
|
+
result: ok({
|
|
9537
|
+
manifest_loaded: true,
|
|
9538
|
+
host_id: resolved.hostId,
|
|
9539
|
+
source: resolved.source,
|
|
9540
|
+
markdown,
|
|
9541
|
+
humanHint: markdown
|
|
9542
|
+
})
|
|
9543
|
+
};
|
|
9544
|
+
}
|
|
9545
|
+
async function loadFleetManifest(file) {
|
|
9546
|
+
let text;
|
|
9547
|
+
try {
|
|
9548
|
+
text = await readFile29(file, "utf8");
|
|
9549
|
+
} catch {
|
|
9550
|
+
return { ok: false, error: "FILE_NOT_FOUND" };
|
|
9551
|
+
}
|
|
9552
|
+
let parsed;
|
|
9553
|
+
try {
|
|
9554
|
+
parsed = yaml3.load(text, { schema: yaml3.JSON_SCHEMA });
|
|
9555
|
+
} catch (error) {
|
|
9556
|
+
return { ok: false, error: "INVALID_YAML", detail: error instanceof Error ? error.message : String(error) };
|
|
9557
|
+
}
|
|
9558
|
+
const result = FleetManifestSchema.safeParse(parsed);
|
|
9559
|
+
if (!result.success) {
|
|
9560
|
+
return { ok: false, error: "INVALID_FLEET_MANIFEST", detail: result.error.issues };
|
|
9561
|
+
}
|
|
9562
|
+
return { ok: true, manifest: result.data };
|
|
9563
|
+
}
|
|
9564
|
+
function invalidFleet(errors) {
|
|
9565
|
+
return {
|
|
9566
|
+
exitCode: ExitCode.FLEET_MANIFEST_INVALID,
|
|
9567
|
+
result: ok({
|
|
9568
|
+
valid: false,
|
|
9569
|
+
errors,
|
|
9570
|
+
warnings: [],
|
|
9571
|
+
host_count: 0,
|
|
9572
|
+
humanHint: `INVALID fleet manifest
|
|
9573
|
+
${errors.map((e) => ` ${e.path || "(root)"}: ${e.message}`).join("\n")}`
|
|
9574
|
+
})
|
|
9575
|
+
};
|
|
9576
|
+
}
|
|
9577
|
+
function fleetLoadErrors(loaded) {
|
|
9578
|
+
if (loaded.error === "INVALID_YAML") {
|
|
9579
|
+
return [{ path: "", message: `invalid YAML: ${String(loaded.detail ?? "parse failed")}` }];
|
|
9580
|
+
}
|
|
9581
|
+
if (loaded.error === "INVALID_FLEET_MANIFEST" && Array.isArray(loaded.detail)) {
|
|
9582
|
+
return loaded.detail.map((issue) => {
|
|
9583
|
+
const zodIssue = issue;
|
|
9584
|
+
return {
|
|
9585
|
+
path: (zodIssue.path ?? []).join("."),
|
|
9586
|
+
message: zodIssue.message ?? "invalid value"
|
|
9587
|
+
};
|
|
9588
|
+
});
|
|
9589
|
+
}
|
|
9590
|
+
return [{ path: "", message: loaded.error }];
|
|
9591
|
+
}
|
|
9592
|
+
function fleetWarnings(manifest) {
|
|
9593
|
+
const warnings = [];
|
|
9594
|
+
for (const [id, host] of Object.entries(manifest.hosts)) {
|
|
9595
|
+
if (host.role === "snapshotter" && host.protected !== true) {
|
|
9596
|
+
warnings.push(`snapshotter host '${id}' is not protected=true`);
|
|
9597
|
+
}
|
|
9598
|
+
}
|
|
9599
|
+
return warnings;
|
|
9600
|
+
}
|
|
9601
|
+
function findSnapshotter(manifest) {
|
|
9602
|
+
return Object.entries(manifest.hosts).find(([, host]) => host.role === "snapshotter")?.[0];
|
|
9603
|
+
}
|
|
9604
|
+
async function resolveHostId(input) {
|
|
9605
|
+
if (input.hostId) return { hostId: input.hostId, source: "host-id" };
|
|
9606
|
+
if (input.env.SKILLWIKI_HOST_ID) return { hostId: input.env.SKILLWIKI_HOST_ID, source: "SKILLWIKI_HOST_ID" };
|
|
9607
|
+
if (input.env.AGENT_HOST_ID) return { hostId: input.env.AGENT_HOST_ID, source: "AGENT_HOST_ID" };
|
|
9608
|
+
if (input.home) {
|
|
9609
|
+
const dotenv = await parseDotenvFile(join45(input.home, ".skillwiki", ".env"));
|
|
9610
|
+
if (dotenv.SKILLWIKI_HOST_ID) {
|
|
9611
|
+
return { hostId: dotenv.SKILLWIKI_HOST_ID, source: "~/.skillwiki/.env:SKILLWIKI_HOST_ID" };
|
|
9612
|
+
}
|
|
9613
|
+
}
|
|
9614
|
+
if (input.env.VS_HOSTNAME) return { hostId: input.env.VS_HOSTNAME, source: "VS_HOSTNAME" };
|
|
9615
|
+
const hostname = input.osHostname.trim();
|
|
9616
|
+
if (hostname) {
|
|
9617
|
+
if (input.manifest.hosts[hostname]) return { hostId: hostname, source: "hostname" };
|
|
9618
|
+
const byHostname = Object.entries(input.manifest.hosts).find(([, host]) => host.identity.hostnames.includes(hostname));
|
|
9619
|
+
if (byHostname) return { hostId: byHostname[0], source: "hostname" };
|
|
9620
|
+
}
|
|
9621
|
+
return {};
|
|
9622
|
+
}
|
|
9623
|
+
function formatKnownContext(input) {
|
|
9624
|
+
const host = input.manifest.hosts[input.hostId];
|
|
9625
|
+
const protectedValue = host.protected === true ? "true" : "false";
|
|
9626
|
+
const writesTo = host.writes_to.join(", ");
|
|
9627
|
+
const selfAliases = collectSelfAliases(input.manifest, input.hostId);
|
|
9628
|
+
const outbound = collectOutboundHosts(input.manifest, input.hostId);
|
|
9629
|
+
const guidance = input.hostId === "macos-dev" ? "use declared SSH aliases for remote work when needed; do not assume undeclared hosts have reciprocal SSH access." : `this session is already on \`${input.hostId}\`; do not SSH to self aliases unless the user explicitly asks. Do not assume outbound SSH to other fleet hosts is configured.`;
|
|
9630
|
+
return [
|
|
9631
|
+
"## Runtime Host Context",
|
|
9632
|
+
"",
|
|
9633
|
+
`- Current machine: \`${input.hostId}\`${input.source ? ` (source: \`${input.source}\`)` : ""}`,
|
|
9634
|
+
`- OS hostname: ${formatMaybe(input.osHostname)}`,
|
|
9635
|
+
`- User: ${formatMaybe(input.user)}`,
|
|
9636
|
+
`- Workspace: ${formatMaybe(input.cwd)}`,
|
|
9637
|
+
`- Vault: ${formatMaybe(input.vault)}`,
|
|
9638
|
+
`- Fleet role: \`${host.role}\`; protected: \`${protectedValue}\`; writes_to: \`${writesTo}\``,
|
|
9639
|
+
`- Self SSH aliases known in fleet: ${formatList(selfAliases)}`,
|
|
9640
|
+
`- Declared outbound SSH from this source: ${outbound.length > 0 ? formatList(outbound) : "none"}`,
|
|
9641
|
+
`- Guidance: ${guidance}`
|
|
9642
|
+
].join("\n");
|
|
9643
|
+
}
|
|
9644
|
+
function formatUnknownContext(input) {
|
|
9645
|
+
return [
|
|
9646
|
+
"## Runtime Host Context",
|
|
9647
|
+
"",
|
|
9648
|
+
"- Current machine: unknown",
|
|
9649
|
+
`- OS hostname: ${formatMaybe(input.osHostname)}`,
|
|
9650
|
+
`- User: ${formatMaybe(input.user)}`,
|
|
9651
|
+
`- Workspace: ${formatMaybe(input.cwd)}`,
|
|
9652
|
+
`- Vault: ${formatMaybe(input.vault)}`,
|
|
9653
|
+
"- Fleet role: unknown",
|
|
9654
|
+
"- Self SSH aliases known in fleet: unknown",
|
|
9655
|
+
"- Declared outbound SSH from this source: unknown",
|
|
9656
|
+
`- Guidance: ${input.reason}; do not assume local vs remote role. Inspect runtime or ask before SSH/deploy/sync work.`
|
|
9657
|
+
].join("\n");
|
|
9658
|
+
}
|
|
9659
|
+
function collectSelfAliases(manifest, hostId2) {
|
|
9660
|
+
const aliases = [];
|
|
9661
|
+
const host = manifest.hosts[hostId2];
|
|
9662
|
+
const access = host?.access?.from ?? {};
|
|
9663
|
+
for (const profile of Object.values(access)) {
|
|
9664
|
+
for (const alias of profile.ssh_aliases ?? []) aliases.push(alias);
|
|
9665
|
+
}
|
|
9666
|
+
return [...new Set(aliases)];
|
|
9667
|
+
}
|
|
9668
|
+
function collectOutboundHosts(manifest, sourceHostId) {
|
|
9669
|
+
const hosts = [];
|
|
9670
|
+
for (const [targetId, target] of Object.entries(manifest.hosts)) {
|
|
9671
|
+
if (targetId === sourceHostId) continue;
|
|
9672
|
+
const profile = target.access?.from?.[sourceHostId];
|
|
9673
|
+
if (profile && (profile.status === "configured" || profile.status === "local")) {
|
|
9674
|
+
hosts.push(targetId);
|
|
9675
|
+
}
|
|
9676
|
+
}
|
|
9677
|
+
return hosts.sort();
|
|
9678
|
+
}
|
|
9679
|
+
function formatList(values) {
|
|
9680
|
+
return values.length > 0 ? values.map((v) => `\`${v}\``).join(", ") : "none";
|
|
9681
|
+
}
|
|
9682
|
+
function formatMaybe(value) {
|
|
9683
|
+
return value && value.trim().length > 0 ? `\`${value}\`` : "unknown";
|
|
9684
|
+
}
|
|
9685
|
+
function safeEnvValue(value) {
|
|
9686
|
+
return value && value.trim().length > 0 ? value : void 0;
|
|
9687
|
+
}
|
|
9688
|
+
function safeUserName() {
|
|
9689
|
+
try {
|
|
9690
|
+
return userInfo().username;
|
|
9691
|
+
} catch {
|
|
9692
|
+
return "";
|
|
9693
|
+
}
|
|
9694
|
+
}
|
|
9695
|
+
|
|
9409
9696
|
// src/utils/auto-commit.ts
|
|
9410
9697
|
import { existsSync as existsSync19 } from "fs";
|
|
9411
|
-
import { join as
|
|
9698
|
+
import { join as join46 } from "path";
|
|
9412
9699
|
async function postCommit(vault, exitCode) {
|
|
9413
9700
|
if (exitCode !== 0) return;
|
|
9414
9701
|
const home = process.env.HOME ?? "";
|
|
9415
9702
|
const dotenv = await parseDotenvFile(configPath(home));
|
|
9416
9703
|
if (dotenv["AUTO_COMMIT"] === "false") return;
|
|
9417
|
-
if (!existsSync19(
|
|
9704
|
+
if (!existsSync19(join46(vault, ".git"))) return;
|
|
9418
9705
|
const lastOps = readLastOp(vault);
|
|
9419
9706
|
if (lastOps.length === 0) return;
|
|
9420
9707
|
const porcelain = git(vault, ["status", "--porcelain"]);
|
|
@@ -9465,7 +9752,7 @@ program.command("validate <file>").description("validate vault page frontmatter
|
|
|
9465
9752
|
emit(await runValidate({ file, apply: !!opts.apply, vault }), vault);
|
|
9466
9753
|
});
|
|
9467
9754
|
program.command("graph").description("graph subcommands").command("build <vault>").option("--out <path>", "graph output path (default: <vault>/.skillwiki/graph.json)").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
9468
|
-
const out = opts.out ??
|
|
9755
|
+
const out = opts.out ?? join47(vault, ".skillwiki", "graph.json");
|
|
9469
9756
|
emit(await runGraphBuild({ vault, out }), vault);
|
|
9470
9757
|
});
|
|
9471
9758
|
var canvasCmd = program.command("canvas").description("manage Obsidian canvas files");
|
|
@@ -9837,6 +10124,22 @@ program.command("ingest <source>").description("ingest a source URL or local fil
|
|
|
9837
10124
|
dryRun: !!opts.dryRun
|
|
9838
10125
|
}), opts.vault);
|
|
9839
10126
|
});
|
|
10127
|
+
var fleetCmd = program.command("fleet").description("manage fleet topology metadata");
|
|
10128
|
+
fleetCmd.command("validate <file>").description("validate a fleet manifest").action(async (file) => {
|
|
10129
|
+
emit(await runFleetValidate({ file }));
|
|
10130
|
+
});
|
|
10131
|
+
fleetCmd.command("context [vault]").description("render compact Runtime Host Context for SessionStart").option("--file <path>", "fleet manifest path").option("--host-id <id>", "explicit current fleet host id").action(async (vault, opts) => {
|
|
10132
|
+
emit(await runFleetContext({
|
|
10133
|
+
vault,
|
|
10134
|
+
file: opts.file,
|
|
10135
|
+
hostId: opts.hostId,
|
|
10136
|
+
env: process.env,
|
|
10137
|
+
home: process.env.HOME ?? "",
|
|
10138
|
+
cwd: process.cwd(),
|
|
10139
|
+
osHostname: process.env.HOSTNAME,
|
|
10140
|
+
user: process.env.USER
|
|
10141
|
+
}));
|
|
10142
|
+
});
|
|
9840
10143
|
for (const w of getDeprecatedWarnings(process.env.HOME ?? "")) {
|
|
9841
10144
|
process.stderr.write(w + "\n");
|
|
9842
10145
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skillwiki",
|
|
3
|
-
"version": "0.8.10-beta.
|
|
3
|
+
"version": "0.8.10-beta.4",
|
|
4
4
|
"skills": "./",
|
|
5
5
|
"description": "Project-aware Karpathy-style knowledge base for Claude Code: 18 prompt-only skills (wiki-*, proj-*, using-skillwiki) backed by the deterministic `skillwiki` CLI.",
|
|
6
6
|
"author": {
|
|
@@ -183,6 +183,43 @@ build_dynamic_session_memory() {
|
|
|
183
183
|
return 0
|
|
184
184
|
}
|
|
185
185
|
|
|
186
|
+
build_runtime_host_context() {
|
|
187
|
+
local vault
|
|
188
|
+
vault=$(resolve_vault_path_for_session)
|
|
189
|
+
|
|
190
|
+
if command -v skillwiki >/dev/null 2>&1; then
|
|
191
|
+
local context
|
|
192
|
+
if [[ -n "$vault" && -d "$vault" ]]; then
|
|
193
|
+
context=$(skillwiki --human fleet context "$vault" 2>/dev/null || true)
|
|
194
|
+
else
|
|
195
|
+
context=$(skillwiki --human fleet context 2>/dev/null || true)
|
|
196
|
+
fi
|
|
197
|
+
if [[ "$context" == *"## Runtime Host Context"* ]]; then
|
|
198
|
+
printf '%s' "$context"
|
|
199
|
+
return 0
|
|
200
|
+
fi
|
|
201
|
+
fi
|
|
202
|
+
|
|
203
|
+
local os_hostname
|
|
204
|
+
local current_user
|
|
205
|
+
os_hostname=$(hostname -s 2>/dev/null || hostname 2>/dev/null || printf '')
|
|
206
|
+
current_user=$(id -un 2>/dev/null || printf '%s' "${USER:-}")
|
|
207
|
+
|
|
208
|
+
cat <<EOF
|
|
209
|
+
## Runtime Host Context
|
|
210
|
+
|
|
211
|
+
- Current machine: unknown
|
|
212
|
+
- OS hostname: \`${os_hostname:-unknown}\`
|
|
213
|
+
- User: \`${current_user:-unknown}\`
|
|
214
|
+
- Workspace: \`${PWD}\`
|
|
215
|
+
- Vault: \`${vault:-unknown}\`
|
|
216
|
+
- Fleet role: unknown
|
|
217
|
+
- Self SSH aliases known in fleet: unknown
|
|
218
|
+
- Declared outbound SSH from this source: unknown
|
|
219
|
+
- Guidance: host identity is unresolved; do not assume local vs remote role. Inspect runtime or ask before SSH/deploy/sync work.
|
|
220
|
+
EOF
|
|
221
|
+
}
|
|
222
|
+
|
|
186
223
|
escape_for_json() {
|
|
187
224
|
local s="$1"
|
|
188
225
|
s="${s//\\/\\\\}"
|
|
@@ -219,12 +256,18 @@ build_skillwiki_session_context() {
|
|
|
219
256
|
local skill_content="$1"
|
|
220
257
|
local project_prd_context
|
|
221
258
|
local dynamic_session_memory
|
|
259
|
+
local runtime_host_context
|
|
222
260
|
local session_context
|
|
223
261
|
|
|
224
262
|
project_prd_context=$(build_project_prd_context)
|
|
225
263
|
dynamic_session_memory=$(build_dynamic_session_memory)
|
|
264
|
+
runtime_host_context=$(build_runtime_host_context)
|
|
226
265
|
|
|
227
266
|
session_context=$'### Skillwiki Activation\n\nSkillwiki is active for this workspace. Below are the capability guidelines for local reference:'
|
|
267
|
+
if [[ -n "$runtime_host_context" ]]; then
|
|
268
|
+
session_context+=$'\n\n'
|
|
269
|
+
session_context+="$runtime_host_context"
|
|
270
|
+
fi
|
|
228
271
|
if [[ -n "$dynamic_session_memory" ]]; then
|
|
229
272
|
session_context+=$'\n\n'
|
|
230
273
|
session_context+="$dynamic_session_memory"
|