yourskills 0.1.4 → 0.1.5
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/README.md +6 -4
- package/dist/main.js +710 -638
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -143,9 +143,10 @@ function configValue(config, key) {
|
|
|
143
143
|
case "server":
|
|
144
144
|
return config.serverUrl ?? UNSET;
|
|
145
145
|
case "track":
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
return String(
|
|
146
|
+
case "autoUpdate": {
|
|
147
|
+
const value = key === "track" ? config.track : config.autoUpdate;
|
|
148
|
+
return String(value !== false);
|
|
149
|
+
}
|
|
149
150
|
}
|
|
150
151
|
}
|
|
151
152
|
function setConfigValue(config, key, value) {
|
|
@@ -2866,8 +2867,8 @@ Options:
|
|
|
2866
2867
|
--fix doctor: repair what it can, and say what it did
|
|
2867
2868
|
--explain config get: say where each value came from
|
|
2868
2869
|
--method <tool> upgrade: npm, bun or brew, when the guess is wrong
|
|
2869
|
-
--track hooks install:
|
|
2870
|
-
--no-track hooks install:
|
|
2870
|
+
--track hooks install: count skill invocations (the default)
|
|
2871
|
+
--no-track hooks install: stop counting skill invocations
|
|
2871
2872
|
--server <url> Registry base URL for this invocation
|
|
2872
2873
|
--plain Plain, greppable output; no frames, no colour
|
|
2873
2874
|
--json Machine-readable output
|
|
@@ -2929,8 +2930,8 @@ var OPTIONS = [
|
|
|
2929
2930
|
["--fix", "doctor: repair what it can"],
|
|
2930
2931
|
["--explain", "config get: where each value came from"],
|
|
2931
2932
|
["--method", "upgrade: npm, bun or brew"],
|
|
2932
|
-
["--track", "hooks install:
|
|
2933
|
-
["--no-track", "hooks install:
|
|
2933
|
+
["--track", "hooks install: count skill invocations (the default)"],
|
|
2934
|
+
["--no-track", "hooks install: stop counting skill invocations"],
|
|
2934
2935
|
["--server", "registry base URL"],
|
|
2935
2936
|
["--plain", "plain output"],
|
|
2936
2937
|
["--json", "machine-readable output"],
|
|
@@ -3098,6 +3099,9 @@ function agentForEvent(kind, event, adapters = HOOK_ADAPTERS) {
|
|
|
3098
3099
|
return owner?.agent ?? "unknown";
|
|
3099
3100
|
}
|
|
3100
3101
|
// src/hooks/audit.ts
|
|
3102
|
+
import { existsSync as existsSync3 } from "node:fs";
|
|
3103
|
+
|
|
3104
|
+
// src/hooks/install.ts
|
|
3101
3105
|
import { existsSync as existsSync2 } from "node:fs";
|
|
3102
3106
|
|
|
3103
3107
|
// src/usage/queue.ts
|
|
@@ -4710,7 +4714,7 @@ function trackingDisabledByEnv() {
|
|
|
4710
4714
|
async function trackingEnabled() {
|
|
4711
4715
|
if (trackingDisabledByEnv())
|
|
4712
4716
|
return false;
|
|
4713
|
-
return (await readConfig()).track
|
|
4717
|
+
return (await readConfig()).track !== false;
|
|
4714
4718
|
}
|
|
4715
4719
|
async function flushQuietly(server) {
|
|
4716
4720
|
try {
|
|
@@ -4864,474 +4868,116 @@ async function applyTo(path, change) {
|
|
|
4864
4868
|
return { changed: true };
|
|
4865
4869
|
}
|
|
4866
4870
|
|
|
4867
|
-
// src/hooks/
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
}
|
|
4871
|
-
|
|
4872
|
-
|
|
4873
|
-
|
|
4874
|
-
|
|
4875
|
-
|
|
4876
|
-
|
|
4877
|
-
|
|
4878
|
-
continue;
|
|
4879
|
-
const expected = wanted(adapter.entries, optedIn);
|
|
4880
|
-
if (expected.length === 0)
|
|
4881
|
-
continue;
|
|
4882
|
-
const file = await readSettingsFile(path);
|
|
4883
|
-
if (!file.ok) {
|
|
4884
|
-
gaps.push({
|
|
4885
|
-
agent: adapter.agent,
|
|
4886
|
-
path,
|
|
4887
|
-
registered: [],
|
|
4888
|
-
missing: [],
|
|
4889
|
-
error: file.error
|
|
4890
|
-
});
|
|
4891
|
-
continue;
|
|
4892
|
-
}
|
|
4893
|
-
const registered = registeredEntries(file.settings, expected).map((entry) => entry.kind);
|
|
4894
|
-
gaps.push({
|
|
4895
|
-
agent: adapter.agent,
|
|
4896
|
-
path,
|
|
4897
|
-
registered,
|
|
4898
|
-
missing: expected.map((entry) => entry.kind).filter((kind) => !registered.includes(kind))
|
|
4899
|
-
});
|
|
4871
|
+
// src/hooks/install.ts
|
|
4872
|
+
var PAYLOAD_DISCLOSURE = [
|
|
4873
|
+
{ mark: "none", text: "Usage tracking sends, once per skill invocation:" },
|
|
4874
|
+
{ mark: "none", text: " skillExternalId", note: "the skill's registry id" },
|
|
4875
|
+
{ mark: "none", text: " ref", note: "the commit you installed it at" },
|
|
4876
|
+
{ mark: "none", text: " agent", note: "which agent ran it" },
|
|
4877
|
+
{ mark: "none", text: " occurredAt", note: "when it ran" },
|
|
4878
|
+
{
|
|
4879
|
+
mark: "none",
|
|
4880
|
+
text: " (nothing else)",
|
|
4881
|
+
note: "the server adds a keyed hash of you; the CLI never sends a user id"
|
|
4900
4882
|
}
|
|
4901
|
-
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
if (!adapter || !entries || entries.length === 0)
|
|
4907
|
-
throw new Error(`nothing to register for ${gap.agent}`);
|
|
4908
|
-
const applied = await applyTo(gap.path, (settings) => addHooks(settings, entries));
|
|
4909
|
-
if (applied.error)
|
|
4910
|
-
throw new Error(applied.error);
|
|
4911
|
-
return `registered ${gap.missing.join(" + ")} in ${gap.path}`;
|
|
4912
|
-
}
|
|
4913
|
-
// ../../packages/bundles/src/drift.ts
|
|
4914
|
-
var SHORT_SHA = 7;
|
|
4915
|
-
function namesSameCommit(prefix, full) {
|
|
4916
|
-
const short = prefix.trim().toLowerCase();
|
|
4917
|
-
const long = full.trim().toLowerCase();
|
|
4918
|
-
if (short === long)
|
|
4919
|
-
return true;
|
|
4920
|
-
return short.length >= SHORT_SHA && long.startsWith(short);
|
|
4921
|
-
}
|
|
4922
|
-
|
|
4923
|
-
// src/installed/installed.ts
|
|
4924
|
-
var shortRef = (ref) => ref.slice(0, SHORT_SHA);
|
|
4925
|
-
function refColumn(row) {
|
|
4926
|
-
return row.unpinned ? "unpinned" : shortRef(row.ref);
|
|
4927
|
-
}
|
|
4928
|
-
function installedRows(nearest) {
|
|
4929
|
-
return Object.values(nearest.manifest.skills).map((entry) => ({
|
|
4930
|
-
name: entry.name,
|
|
4931
|
-
ref: entry.ref,
|
|
4932
|
-
bundle: entry.bundle ?? null,
|
|
4933
|
-
scope: nearest.manifest.scope,
|
|
4934
|
-
unpinned: entry.unpinned === true
|
|
4935
|
-
})).sort((a, b) => a.name.localeCompare(b.name));
|
|
4883
|
+
];
|
|
4884
|
+
function trackNote(track) {
|
|
4885
|
+
if (trackingDisabledByEnv())
|
|
4886
|
+
return "off (YOURSKILLS_NO_TRACK is set)";
|
|
4887
|
+
return track ? "on; turn off with `yourskills config set track false`" : "off; turn on with `yourskills config set track true`";
|
|
4936
4888
|
}
|
|
4937
|
-
function
|
|
4938
|
-
|
|
4939
|
-
const bundles = Math.max(...rows.map((r) => (r.bundle ?? "-").length));
|
|
4940
|
-
return rows.map((row) => ({
|
|
4941
|
-
mark: "none",
|
|
4942
|
-
text: `${row.name.padEnd(names)} ${refColumn(row)}`,
|
|
4943
|
-
note: `${(row.bundle ?? "-").padEnd(bundles)} ${row.scope}`
|
|
4944
|
-
}));
|
|
4889
|
+
function trackingOn(config) {
|
|
4890
|
+
return !trackingDisabledByEnv() && config.track !== false;
|
|
4945
4891
|
}
|
|
4946
|
-
function
|
|
4947
|
-
|
|
4948
|
-
|
|
4949
|
-
|
|
4950
|
-
|
|
4951
|
-
|
|
4952
|
-
|
|
4953
|
-
}
|
|
4954
|
-
] : columns(rows);
|
|
4892
|
+
function selectAdapters(agent) {
|
|
4893
|
+
if (agent) {
|
|
4894
|
+
const found = adapterFor(agent);
|
|
4895
|
+
if (!found)
|
|
4896
|
+
return { adapters: [], unknown: [agent], absent: [] };
|
|
4897
|
+
return existsSync2(found.home()) ? { adapters: [found], unknown: [], absent: [] } : { adapters: [], unknown: [], absent: [found] };
|
|
4898
|
+
}
|
|
4955
4899
|
return {
|
|
4956
|
-
|
|
4957
|
-
|
|
4958
|
-
|
|
4959
|
-
manifest: nearest.path,
|
|
4960
|
-
scope: nearest.manifest.scope,
|
|
4961
|
-
skills: rows
|
|
4962
|
-
},
|
|
4963
|
-
...rows.length === 0 ? {
|
|
4964
|
-
next: {
|
|
4965
|
-
command: "yourskills browse",
|
|
4966
|
-
why: "pick something to install"
|
|
4967
|
-
}
|
|
4968
|
-
} : {}
|
|
4900
|
+
adapters: HOOK_ADAPTERS.filter((a) => existsSync2(a.home())),
|
|
4901
|
+
unknown: [],
|
|
4902
|
+
absent: []
|
|
4969
4903
|
};
|
|
4970
4904
|
}
|
|
4971
|
-
|
|
4972
|
-
|
|
4973
|
-
|
|
4974
|
-
|
|
4975
|
-
|
|
4976
|
-
return
|
|
4905
|
+
async function pinnedAdapters() {
|
|
4906
|
+
const pinned = await resolveAgents();
|
|
4907
|
+
if (pinned.length === 0)
|
|
4908
|
+
return null;
|
|
4909
|
+
const adapters = pinned.map((name) => adapterFor(name)).filter((a) => a !== undefined).filter((a) => existsSync2(a.home()));
|
|
4910
|
+
return adapters.length > 0 ? adapters : null;
|
|
4977
4911
|
}
|
|
4978
|
-
function
|
|
4979
|
-
const
|
|
4980
|
-
const
|
|
4981
|
-
|
|
4982
|
-
|
|
4983
|
-
|
|
4984
|
-
|
|
4985
|
-
|
|
4986
|
-
|
|
4987
|
-
|
|
4912
|
+
async function installInto(adapter, tracking) {
|
|
4913
|
+
const path = adapter.settingsPath();
|
|
4914
|
+
const result = {
|
|
4915
|
+
agent: adapter.agent,
|
|
4916
|
+
path,
|
|
4917
|
+
installed: [],
|
|
4918
|
+
already: [],
|
|
4919
|
+
skipped: []
|
|
4920
|
+
};
|
|
4921
|
+
if (!path) {
|
|
4922
|
+
for (const kind of adapter.supports) {
|
|
4923
|
+
result.skipped.push({
|
|
4924
|
+
kind,
|
|
4925
|
+
why: adapter.limitation ?? "not supported yet"
|
|
4926
|
+
});
|
|
4988
4927
|
}
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
|
|
4928
|
+
return result;
|
|
4929
|
+
}
|
|
4930
|
+
const wanted = [];
|
|
4931
|
+
for (const entry of adapter.entries) {
|
|
4932
|
+
if (entry.kind === "usage" && !tracking) {
|
|
4933
|
+
result.skipped.push({ kind: entry.kind, why: trackNote(false) });
|
|
4992
4934
|
continue;
|
|
4993
4935
|
}
|
|
4994
|
-
|
|
4995
|
-
const available = pin?.pinnedSha ?? row.currentSha;
|
|
4996
|
-
if (!hasMoved(entry.ref, available))
|
|
4997
|
-
continue;
|
|
4998
|
-
moved.push({
|
|
4999
|
-
name: entry.name,
|
|
5000
|
-
bundle: entry.bundle ?? null,
|
|
5001
|
-
installed: entry.ref,
|
|
5002
|
-
available,
|
|
5003
|
-
deprecated: pin?.deprecated ?? row.status === "deprecated"
|
|
5004
|
-
});
|
|
4936
|
+
wanted.push(entry);
|
|
5005
4937
|
}
|
|
5006
|
-
|
|
5007
|
-
|
|
5008
|
-
|
|
5009
|
-
|
|
5010
|
-
|
|
5011
|
-
|
|
5012
|
-
|
|
5013
|
-
|
|
5014
|
-
return drift(nearest.manifest, pins, catalog);
|
|
5015
|
-
}
|
|
5016
|
-
async function outdatedReport(client, nearest, quiet = false) {
|
|
5017
|
-
let report2;
|
|
5018
|
-
try {
|
|
5019
|
-
report2 = await driftNow(client, nearest);
|
|
5020
|
-
} catch (error) {
|
|
5021
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
5022
|
-
return {
|
|
5023
|
-
title: "outdated",
|
|
5024
|
-
lines: [
|
|
5025
|
-
{ mark: "fail", text: "Could not reach the registry.", note: message }
|
|
5026
|
-
],
|
|
5027
|
-
json: { manifest: nearest.path, error: message },
|
|
5028
|
-
silent: quiet
|
|
5029
|
-
};
|
|
4938
|
+
let present = new Set;
|
|
4939
|
+
const applied = await applyTo(path, (settings) => {
|
|
4940
|
+
present = new Set(registeredEntries(settings, wanted).map((entry) => entry.kind));
|
|
4941
|
+
return addHooks(settings, wanted);
|
|
4942
|
+
});
|
|
4943
|
+
if (applied.error) {
|
|
4944
|
+
result.error = applied.error;
|
|
4945
|
+
return result;
|
|
5030
4946
|
}
|
|
5031
|
-
const
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
|
|
5036
|
-
|
|
4947
|
+
for (const entry of wanted) {
|
|
4948
|
+
if (present.has(entry.kind))
|
|
4949
|
+
result.already.push(entry.kind);
|
|
4950
|
+
else
|
|
4951
|
+
result.installed.push(entry.kind);
|
|
4952
|
+
}
|
|
4953
|
+
if (applied.changed && result.already.length > 0) {
|
|
4954
|
+
result.repaired = result.already;
|
|
4955
|
+
result.already = [];
|
|
4956
|
+
}
|
|
4957
|
+
return result;
|
|
4958
|
+
}
|
|
4959
|
+
function linesFor(result) {
|
|
4960
|
+
const lines = [];
|
|
4961
|
+
if (result.error) {
|
|
5037
4962
|
lines.push({
|
|
5038
4963
|
mark: "fail",
|
|
5039
|
-
text:
|
|
5040
|
-
note:
|
|
4964
|
+
text: `${result.agent}: left untouched`,
|
|
4965
|
+
note: result.error
|
|
5041
4966
|
});
|
|
4967
|
+
return lines;
|
|
5042
4968
|
}
|
|
5043
|
-
for (const
|
|
4969
|
+
for (const kind of result.installed) {
|
|
5044
4970
|
lines.push({
|
|
5045
|
-
mark: "
|
|
5046
|
-
text:
|
|
5047
|
-
note:
|
|
4971
|
+
mark: "ok",
|
|
4972
|
+
text: `${result.agent} ${kind}`,
|
|
4973
|
+
note: result.path ?? ""
|
|
5048
4974
|
});
|
|
5049
4975
|
}
|
|
5050
|
-
const
|
|
5051
|
-
if (current && !quiet) {
|
|
4976
|
+
for (const kind of result.repaired ?? []) {
|
|
5052
4977
|
lines.push({
|
|
5053
4978
|
mark: "ok",
|
|
5054
|
-
text:
|
|
5055
|
-
|
|
5056
|
-
}
|
|
5057
|
-
return {
|
|
5058
|
-
title: "outdated",
|
|
5059
|
-
lines,
|
|
5060
|
-
json: {
|
|
5061
|
-
manifest: nearest.path,
|
|
5062
|
-
outdated: report2.moved,
|
|
5063
|
-
unknown: report2.unknown,
|
|
5064
|
-
unpinned: report2.unpinned
|
|
5065
|
-
},
|
|
5066
|
-
silent: quiet && current,
|
|
5067
|
-
...report2.moved.length > 0 ? {
|
|
5068
|
-
next: {
|
|
5069
|
-
command: "yourskills update --all",
|
|
5070
|
-
why: "install what moved"
|
|
5071
|
-
}
|
|
5072
|
-
} : {}
|
|
5073
|
-
};
|
|
5074
|
-
}
|
|
5075
|
-
// src/hooks/emit.ts
|
|
5076
|
-
function describe(outcome) {
|
|
5077
|
-
switch (outcome.kind) {
|
|
5078
|
-
case "quiet":
|
|
5079
|
-
return [];
|
|
5080
|
-
case "signed-out":
|
|
5081
|
-
return [
|
|
5082
|
-
"yourskills: your sign-in has expired, so your skills are no longer kept up to date.",
|
|
5083
|
-
" run: yourskills login"
|
|
5084
|
-
];
|
|
5085
|
-
case "behind": {
|
|
5086
|
-
const lines = [
|
|
5087
|
-
`yourskills: ${count(outcome.moved.length, "skill")} ${outcome.moved.length === 1 ? "has" : "have"} a newer version in your registry (autoUpdate is off)`
|
|
5088
|
-
];
|
|
5089
|
-
for (const row of outcome.moved) {
|
|
5090
|
-
lines.push(` ${row.name} ${shortRef(row.installed)} -> ${shortRef(row.available)}${column(driftTag(row))}`);
|
|
5091
|
-
}
|
|
5092
|
-
lines.push(" run: yourskills update --all");
|
|
5093
|
-
return lines;
|
|
5094
|
-
}
|
|
5095
|
-
case "updated": {
|
|
5096
|
-
const { applied, failed } = outcome;
|
|
5097
|
-
const lines = [
|
|
5098
|
-
applied.length > 0 ? `yourskills: updated ${count(applied.length, "skill")} to what your registry pins; ${applied.length === 1 ? "it is" : "they are"} live in this session.` : `yourskills: could not update ${count(failed.length, "skill")}`
|
|
5099
|
-
];
|
|
5100
|
-
for (const row of applied) {
|
|
5101
|
-
lines.push(` ${row.name} ${row.from} -> ${row.to}`);
|
|
5102
|
-
}
|
|
5103
|
-
for (const row of failed) {
|
|
5104
|
-
lines.push(` ${row.name} failed: ${row.message}`);
|
|
5105
|
-
}
|
|
5106
|
-
if (applied.length > 0)
|
|
5107
|
-
lines.push(` recorded in ${outcome.manifest}`);
|
|
5108
|
-
if (failed.length > 0)
|
|
5109
|
-
lines.push(" run: yourskills doctor");
|
|
5110
|
-
return lines;
|
|
5111
|
-
}
|
|
5112
|
-
}
|
|
5113
|
-
}
|
|
5114
|
-
function describeForModel(outcome) {
|
|
5115
|
-
switch (outcome.kind) {
|
|
5116
|
-
case "quiet":
|
|
5117
|
-
return [];
|
|
5118
|
-
case "signed-out":
|
|
5119
|
-
return [
|
|
5120
|
-
"yourskills: the user's session with the skills registry has expired, so installed skills are no longer being updated. The user has been told to run `yourskills login`; it is their call."
|
|
5121
|
-
];
|
|
5122
|
-
case "behind": {
|
|
5123
|
-
const lines = [
|
|
5124
|
-
`yourskills: ${count(outcome.moved.length, "installed skill")} ${outcome.moved.length === 1 ? "is" : "are"} behind the registry. Automatic updates are off; the user has been told and it is their call whether to run \`yourskills update --all\`.`
|
|
5125
|
-
];
|
|
5126
|
-
for (const row of outcome.moved) {
|
|
5127
|
-
lines.push(` ${row.name} installed ${shortRef(row.installed)}, registry pins ${shortRef(row.available)}${column(driftTag(row))}`);
|
|
5128
|
-
}
|
|
5129
|
-
return lines;
|
|
5130
|
-
}
|
|
5131
|
-
case "updated": {
|
|
5132
|
-
const { applied, failed } = outcome;
|
|
5133
|
-
const lines = [];
|
|
5134
|
-
if (applied.length > 0) {
|
|
5135
|
-
lines.push(`yourskills updated ${count(applied.length, "installed skill")} on disk at the start of this session. The contents changed: re-read a skill before relying on what you remember of it.`);
|
|
5136
|
-
for (const row of applied) {
|
|
5137
|
-
lines.push(` ${row.name} ${row.from} -> ${row.to}`);
|
|
5138
|
-
}
|
|
5139
|
-
lines.push(` recorded in ${outcome.manifest}`);
|
|
5140
|
-
}
|
|
5141
|
-
if (failed.length > 0) {
|
|
5142
|
-
lines.push(`yourskills could not update ${count(failed.length, "installed skill")}; the version on disk is unchanged. The user has been told.`);
|
|
5143
|
-
for (const row of failed) {
|
|
5144
|
-
lines.push(` ${row.name} ${row.message}`);
|
|
5145
|
-
}
|
|
5146
|
-
}
|
|
5147
|
-
return lines;
|
|
5148
|
-
}
|
|
5149
|
-
}
|
|
5150
|
-
}
|
|
5151
|
-
function count(n, noun) {
|
|
5152
|
-
return `${n} ${noun}${n === 1 ? "" : "s"}`;
|
|
5153
|
-
}
|
|
5154
|
-
function column(text) {
|
|
5155
|
-
return text ? ` ${text}` : "";
|
|
5156
|
-
}
|
|
5157
|
-
function emit(outcome) {
|
|
5158
|
-
const lines = describe(outcome);
|
|
5159
|
-
if (lines.length === 0)
|
|
5160
|
-
return;
|
|
5161
|
-
const installed = outcome.kind === "updated" && outcome.applied.length > 0;
|
|
5162
|
-
console.log(JSON.stringify({
|
|
5163
|
-
systemMessage: lines.join(`
|
|
5164
|
-
`),
|
|
5165
|
-
hookSpecificOutput: {
|
|
5166
|
-
hookEventName: "SessionStart",
|
|
5167
|
-
additionalContext: describeForModel(outcome).join(`
|
|
5168
|
-
`),
|
|
5169
|
-
...installed ? { reloadSkills: true } : {}
|
|
5170
|
-
}
|
|
5171
|
-
}));
|
|
5172
|
-
}
|
|
5173
|
-
// src/hooks/ensure.ts
|
|
5174
|
-
import { existsSync as existsSync3 } from "node:fs";
|
|
5175
|
-
async function registerInto(adapter) {
|
|
5176
|
-
const wanted2 = adapter.entries.filter((entry) => entry.kind === "update-check");
|
|
5177
|
-
if (wanted2.length === 0)
|
|
5178
|
-
return null;
|
|
5179
|
-
const path = adapter.settingsPath();
|
|
5180
|
-
if (!path)
|
|
5181
|
-
return null;
|
|
5182
|
-
if (!existsSync3(adapter.home()))
|
|
5183
|
-
return null;
|
|
5184
|
-
const applied = await applyTo(path, (settings) => addHooks(settings, wanted2));
|
|
5185
|
-
if (!applied.changed)
|
|
5186
|
-
return null;
|
|
5187
|
-
return { agent: adapter.agent, path, entries: wanted2 };
|
|
5188
|
-
}
|
|
5189
|
-
function linesFor(registered) {
|
|
5190
|
-
const agents = registered.map((r) => r.agent).join(", ");
|
|
5191
|
-
const lines = [
|
|
5192
|
-
`+ yourskills will check your skills once a day, when ${agents} starts, and update what moved`
|
|
5193
|
-
];
|
|
5194
|
-
for (const { agent, path } of registered) {
|
|
5195
|
-
lines.push(` ${agent} ${path}`);
|
|
5196
|
-
}
|
|
5197
|
-
lines.push(" undo with: yourskills hooks uninstall");
|
|
5198
|
-
if (!trackingDisabledByEnv()) {
|
|
5199
|
-
lines.push(" usage counting stays off; turn it on with: yourskills hooks install --track");
|
|
5200
|
-
}
|
|
5201
|
-
return lines;
|
|
5202
|
-
}
|
|
5203
|
-
async function ensureHooksRegistered() {
|
|
5204
|
-
try {
|
|
5205
|
-
const config = await readConfig();
|
|
5206
|
-
if (config.hooksOffered)
|
|
5207
|
-
return [];
|
|
5208
|
-
if (!config.token)
|
|
5209
|
-
return [];
|
|
5210
|
-
const registered = [];
|
|
5211
|
-
for (const adapter of HOOK_ADAPTERS) {
|
|
5212
|
-
try {
|
|
5213
|
-
const result = await registerInto(adapter);
|
|
5214
|
-
if (result)
|
|
5215
|
-
registered.push(result);
|
|
5216
|
-
} catch {}
|
|
5217
|
-
}
|
|
5218
|
-
await updateConfig((stored) => ({
|
|
5219
|
-
...stored,
|
|
5220
|
-
hooksOffered: new Date().toISOString()
|
|
5221
|
-
}));
|
|
5222
|
-
return registered.length > 0 ? linesFor(registered) : [];
|
|
5223
|
-
} catch {
|
|
5224
|
-
return [];
|
|
5225
|
-
}
|
|
5226
|
-
}
|
|
5227
|
-
// src/hooks/install.ts
|
|
5228
|
-
import { existsSync as existsSync4 } from "node:fs";
|
|
5229
|
-
var PAYLOAD_DISCLOSURE = [
|
|
5230
|
-
{ mark: "none", text: "Usage tracking sends, once per skill invocation:" },
|
|
5231
|
-
{ mark: "none", text: " skillExternalId", note: "the skill's registry id" },
|
|
5232
|
-
{ mark: "none", text: " ref", note: "the commit you installed it at" },
|
|
5233
|
-
{ mark: "none", text: " agent", note: "which agent ran it" },
|
|
5234
|
-
{ mark: "none", text: " occurredAt", note: "when it ran" },
|
|
5235
|
-
{
|
|
5236
|
-
mark: "none",
|
|
5237
|
-
text: " (nothing else)",
|
|
5238
|
-
note: "the server adds a keyed hash of you; the CLI never sends a user id"
|
|
5239
|
-
}
|
|
5240
|
-
];
|
|
5241
|
-
function trackNote(track) {
|
|
5242
|
-
if (trackingDisabledByEnv())
|
|
5243
|
-
return "off (YOURSKILLS_NO_TRACK is set)";
|
|
5244
|
-
return track ? "on" : "off — enable with `yourskills hooks install --track`";
|
|
5245
|
-
}
|
|
5246
|
-
function selectAdapters(agent) {
|
|
5247
|
-
if (agent) {
|
|
5248
|
-
const found = adapterFor(agent);
|
|
5249
|
-
if (!found)
|
|
5250
|
-
return { adapters: [], unknown: [agent], absent: [] };
|
|
5251
|
-
return existsSync4(found.home()) ? { adapters: [found], unknown: [], absent: [] } : { adapters: [], unknown: [], absent: [found] };
|
|
5252
|
-
}
|
|
5253
|
-
return {
|
|
5254
|
-
adapters: HOOK_ADAPTERS.filter((a) => existsSync4(a.home())),
|
|
5255
|
-
unknown: [],
|
|
5256
|
-
absent: []
|
|
5257
|
-
};
|
|
5258
|
-
}
|
|
5259
|
-
async function pinnedAdapters() {
|
|
5260
|
-
const pinned = await resolveAgents();
|
|
5261
|
-
if (pinned.length === 0)
|
|
5262
|
-
return null;
|
|
5263
|
-
const adapters = pinned.map((name) => adapterFor(name)).filter((a) => a !== undefined).filter((a) => existsSync4(a.home()));
|
|
5264
|
-
return adapters.length > 0 ? adapters : null;
|
|
5265
|
-
}
|
|
5266
|
-
async function installInto(adapter, optedIn) {
|
|
5267
|
-
const path = adapter.settingsPath();
|
|
5268
|
-
const result = {
|
|
5269
|
-
agent: adapter.agent,
|
|
5270
|
-
path,
|
|
5271
|
-
installed: [],
|
|
5272
|
-
already: [],
|
|
5273
|
-
skipped: []
|
|
5274
|
-
};
|
|
5275
|
-
if (!path) {
|
|
5276
|
-
for (const kind of adapter.supports) {
|
|
5277
|
-
result.skipped.push({
|
|
5278
|
-
kind,
|
|
5279
|
-
why: adapter.limitation ?? "not supported yet"
|
|
5280
|
-
});
|
|
5281
|
-
}
|
|
5282
|
-
return result;
|
|
5283
|
-
}
|
|
5284
|
-
const wanted2 = [];
|
|
5285
|
-
for (const entry of adapter.entries) {
|
|
5286
|
-
if (entry.kind === "usage" && !optedIn) {
|
|
5287
|
-
result.skipped.push({ kind: entry.kind, why: trackNote(false) });
|
|
5288
|
-
continue;
|
|
5289
|
-
}
|
|
5290
|
-
wanted2.push(entry);
|
|
5291
|
-
}
|
|
5292
|
-
let present = new Set;
|
|
5293
|
-
const applied = await applyTo(path, (settings) => {
|
|
5294
|
-
present = new Set(registeredEntries(settings, wanted2).map((entry) => entry.kind));
|
|
5295
|
-
return addHooks(settings, wanted2);
|
|
5296
|
-
});
|
|
5297
|
-
if (applied.error) {
|
|
5298
|
-
result.error = applied.error;
|
|
5299
|
-
return result;
|
|
5300
|
-
}
|
|
5301
|
-
for (const entry of wanted2) {
|
|
5302
|
-
if (present.has(entry.kind))
|
|
5303
|
-
result.already.push(entry.kind);
|
|
5304
|
-
else
|
|
5305
|
-
result.installed.push(entry.kind);
|
|
5306
|
-
}
|
|
5307
|
-
if (applied.changed && result.already.length > 0) {
|
|
5308
|
-
result.repaired = result.already;
|
|
5309
|
-
result.already = [];
|
|
5310
|
-
}
|
|
5311
|
-
return result;
|
|
5312
|
-
}
|
|
5313
|
-
function linesFor2(result) {
|
|
5314
|
-
const lines = [];
|
|
5315
|
-
if (result.error) {
|
|
5316
|
-
lines.push({
|
|
5317
|
-
mark: "fail",
|
|
5318
|
-
text: `${result.agent}: left untouched`,
|
|
5319
|
-
note: result.error
|
|
5320
|
-
});
|
|
5321
|
-
return lines;
|
|
5322
|
-
}
|
|
5323
|
-
for (const kind of result.installed) {
|
|
5324
|
-
lines.push({
|
|
5325
|
-
mark: "ok",
|
|
5326
|
-
text: `${result.agent} ${kind}`,
|
|
5327
|
-
note: result.path ?? ""
|
|
5328
|
-
});
|
|
5329
|
-
}
|
|
5330
|
-
for (const kind of result.repaired ?? []) {
|
|
5331
|
-
lines.push({
|
|
5332
|
-
mark: "ok",
|
|
5333
|
-
text: `${result.agent} ${kind}`,
|
|
5334
|
-
note: `updated ${result.path ?? ""}`.trim()
|
|
4979
|
+
text: `${result.agent} ${kind}`,
|
|
4980
|
+
note: `updated ${result.path ?? ""}`.trim()
|
|
5335
4981
|
});
|
|
5336
4982
|
}
|
|
5337
4983
|
for (const kind of result.already) {
|
|
@@ -5356,11 +5002,11 @@ async function hooksInstall(opts = {}) {
|
|
|
5356
5002
|
await writeConfig({ ...config2, track: opts.track });
|
|
5357
5003
|
}
|
|
5358
5004
|
const config = await readConfig();
|
|
5359
|
-
const
|
|
5005
|
+
const tracking = trackingOn(config);
|
|
5360
5006
|
const { adapters: selected, unknown, absent } = selectAdapters(opts.agent);
|
|
5361
5007
|
const adapters = opts.agent === undefined ? await pinnedAdapters() ?? selected : selected;
|
|
5362
5008
|
const lines = [...PAYLOAD_DISCLOSURE];
|
|
5363
|
-
lines.push({ mark: "none", text: "tracking", note: trackNote(
|
|
5009
|
+
lines.push({ mark: "none", text: "tracking", note: trackNote(tracking) });
|
|
5364
5010
|
for (const agent of unknown) {
|
|
5365
5011
|
lines.push({
|
|
5366
5012
|
mark: "fail",
|
|
@@ -5385,9 +5031,9 @@ async function hooksInstall(opts = {}) {
|
|
|
5385
5031
|
}
|
|
5386
5032
|
const results = [];
|
|
5387
5033
|
for (const adapter of adapters) {
|
|
5388
|
-
const result = await installInto(adapter,
|
|
5034
|
+
const result = await installInto(adapter, tracking);
|
|
5389
5035
|
results.push(result);
|
|
5390
|
-
lines.push(...
|
|
5036
|
+
lines.push(...linesFor(result));
|
|
5391
5037
|
}
|
|
5392
5038
|
if (adapters.length === 0 && unknown.length === 0 && absent.length === 0) {
|
|
5393
5039
|
lines.push({
|
|
@@ -5400,7 +5046,7 @@ async function hooksInstall(opts = {}) {
|
|
|
5400
5046
|
title: "hooks install",
|
|
5401
5047
|
lines,
|
|
5402
5048
|
json: {
|
|
5403
|
-
track:
|
|
5049
|
+
track: tracking,
|
|
5404
5050
|
agents: results,
|
|
5405
5051
|
unknown,
|
|
5406
5052
|
absent: absent.map((a) => a.agent)
|
|
@@ -5451,9 +5097,9 @@ async function hooksUninstall(opts = {}) {
|
|
|
5451
5097
|
}
|
|
5452
5098
|
async function hooksStatus() {
|
|
5453
5099
|
const config = await readConfig();
|
|
5454
|
-
const
|
|
5100
|
+
const tracking = trackingOn(config);
|
|
5455
5101
|
const lines = [
|
|
5456
|
-
{ mark: "none", text: "tracking", note: trackNote(
|
|
5102
|
+
{ mark: "none", text: "tracking", note: trackNote(tracking) }
|
|
5457
5103
|
];
|
|
5458
5104
|
const agents = [];
|
|
5459
5105
|
for (const agent of agentsToReport(config.agents ?? [])) {
|
|
@@ -5508,194 +5154,392 @@ async function hooksStatus() {
|
|
|
5508
5154
|
return {
|
|
5509
5155
|
title: "hooks status",
|
|
5510
5156
|
lines,
|
|
5511
|
-
json: { track:
|
|
5157
|
+
json: { track: tracking, agents }
|
|
5512
5158
|
};
|
|
5513
5159
|
}
|
|
5514
|
-
|
|
5515
|
-
|
|
5516
|
-
|
|
5517
|
-
|
|
5518
|
-
|
|
5519
|
-
|
|
5520
|
-
readConfig
|
|
5521
|
-
|
|
5522
|
-
|
|
5523
|
-
|
|
5524
|
-
|
|
5525
|
-
|
|
5526
|
-
|
|
5527
|
-
|
|
5528
|
-
|
|
5529
|
-
|
|
5530
|
-
|
|
5531
|
-
|
|
5160
|
+
|
|
5161
|
+
// src/hooks/audit.ts
|
|
5162
|
+
function wanted(entries, tracking) {
|
|
5163
|
+
return entries.filter((entry) => entry.kind === "update-check" || tracking);
|
|
5164
|
+
}
|
|
5165
|
+
async function hookGaps() {
|
|
5166
|
+
const config = await readConfig();
|
|
5167
|
+
const optedIn = trackingOn(config);
|
|
5168
|
+
const gaps = [];
|
|
5169
|
+
for (const adapter of HOOK_ADAPTERS) {
|
|
5170
|
+
const path = adapter.settingsPath();
|
|
5171
|
+
if (!path || !existsSync3(adapter.home()))
|
|
5172
|
+
continue;
|
|
5173
|
+
const expected = wanted(adapter.entries, optedIn);
|
|
5174
|
+
if (expected.length === 0)
|
|
5175
|
+
continue;
|
|
5176
|
+
const file = await readSettingsFile(path);
|
|
5177
|
+
if (!file.ok) {
|
|
5178
|
+
gaps.push({
|
|
5179
|
+
agent: adapter.agent,
|
|
5180
|
+
path,
|
|
5181
|
+
registered: [],
|
|
5182
|
+
missing: [],
|
|
5183
|
+
error: file.error
|
|
5184
|
+
});
|
|
5185
|
+
continue;
|
|
5186
|
+
}
|
|
5187
|
+
const registered = registeredEntries(file.settings, expected).map((entry) => entry.kind);
|
|
5188
|
+
gaps.push({
|
|
5189
|
+
agent: adapter.agent,
|
|
5190
|
+
path,
|
|
5191
|
+
registered,
|
|
5192
|
+
missing: expected.map((entry) => entry.kind).filter((kind) => !registered.includes(kind))
|
|
5193
|
+
});
|
|
5194
|
+
}
|
|
5195
|
+
return gaps;
|
|
5196
|
+
}
|
|
5197
|
+
async function registerMissing(gap) {
|
|
5198
|
+
const adapter = HOOK_ADAPTERS.find((a) => a.agent === gap.agent);
|
|
5199
|
+
const entries = adapter?.entries.filter((entry) => gap.missing.includes(entry.kind));
|
|
5200
|
+
if (!adapter || !entries || entries.length === 0)
|
|
5201
|
+
throw new Error(`nothing to register for ${gap.agent}`);
|
|
5202
|
+
const applied = await applyTo(gap.path, (settings) => addHooks(settings, entries));
|
|
5203
|
+
if (applied.error)
|
|
5204
|
+
throw new Error(applied.error);
|
|
5205
|
+
return `registered ${gap.missing.join(" + ")} in ${gap.path}`;
|
|
5206
|
+
}
|
|
5207
|
+
// ../../packages/bundles/src/drift.ts
|
|
5208
|
+
var SHORT_SHA = 7;
|
|
5209
|
+
function namesSameCommit(prefix, full) {
|
|
5210
|
+
const short = prefix.trim().toLowerCase();
|
|
5211
|
+
const long = full.trim().toLowerCase();
|
|
5212
|
+
if (short === long)
|
|
5532
5213
|
return true;
|
|
5533
|
-
|
|
5534
|
-
return Number.isNaN(at) || now - at >= DAY_MS;
|
|
5214
|
+
return short.length >= SHORT_SHA && long.startsWith(short);
|
|
5535
5215
|
}
|
|
5536
|
-
|
|
5537
|
-
|
|
5538
|
-
|
|
5539
|
-
|
|
5540
|
-
|
|
5541
|
-
timer.unref?.();
|
|
5542
|
-
})
|
|
5543
|
-
]);
|
|
5216
|
+
|
|
5217
|
+
// src/installed/installed.ts
|
|
5218
|
+
var shortRef = (ref) => ref.slice(0, SHORT_SHA);
|
|
5219
|
+
function refColumn(row) {
|
|
5220
|
+
return row.unpinned ? "unpinned" : shortRef(row.ref);
|
|
5544
5221
|
}
|
|
5545
|
-
|
|
5546
|
-
|
|
5547
|
-
|
|
5548
|
-
|
|
5549
|
-
|
|
5550
|
-
|
|
5551
|
-
|
|
5222
|
+
function installedRows(nearest) {
|
|
5223
|
+
return Object.values(nearest.manifest.skills).map((entry) => ({
|
|
5224
|
+
name: entry.name,
|
|
5225
|
+
ref: entry.ref,
|
|
5226
|
+
bundle: entry.bundle ?? null,
|
|
5227
|
+
scope: nearest.manifest.scope,
|
|
5228
|
+
unpinned: entry.unpinned === true
|
|
5229
|
+
})).sort((a, b) => a.name.localeCompare(b.name));
|
|
5552
5230
|
}
|
|
5553
|
-
|
|
5554
|
-
const
|
|
5555
|
-
|
|
5556
|
-
|
|
5557
|
-
|
|
5558
|
-
}
|
|
5559
|
-
|
|
5560
|
-
|
|
5561
|
-
|
|
5562
|
-
|
|
5563
|
-
|
|
5564
|
-
|
|
5565
|
-
|
|
5566
|
-
|
|
5567
|
-
|
|
5568
|
-
|
|
5569
|
-
|
|
5570
|
-
|
|
5571
|
-
|
|
5572
|
-
|
|
5573
|
-
|
|
5574
|
-
|
|
5575
|
-
|
|
5576
|
-
|
|
5577
|
-
|
|
5231
|
+
function columns(rows) {
|
|
5232
|
+
const names = Math.max(...rows.map((r) => r.name.length));
|
|
5233
|
+
const bundles = Math.max(...rows.map((r) => (r.bundle ?? "-").length));
|
|
5234
|
+
return rows.map((row) => ({
|
|
5235
|
+
mark: "none",
|
|
5236
|
+
text: `${row.name.padEnd(names)} ${refColumn(row)}`,
|
|
5237
|
+
note: `${(row.bundle ?? "-").padEnd(bundles)} ${row.scope}`
|
|
5238
|
+
}));
|
|
5239
|
+
}
|
|
5240
|
+
function installedReport(nearest) {
|
|
5241
|
+
const rows = installedRows(nearest);
|
|
5242
|
+
const lines = rows.length === 0 ? [
|
|
5243
|
+
{
|
|
5244
|
+
mark: "none",
|
|
5245
|
+
text: "Nothing installed. Run `yourskills add <name>`.",
|
|
5246
|
+
note: nearest.path
|
|
5247
|
+
}
|
|
5248
|
+
] : columns(rows);
|
|
5249
|
+
return {
|
|
5250
|
+
title: "installed",
|
|
5251
|
+
lines,
|
|
5252
|
+
json: {
|
|
5253
|
+
manifest: nearest.path,
|
|
5254
|
+
scope: nearest.manifest.scope,
|
|
5255
|
+
skills: rows
|
|
5256
|
+
},
|
|
5257
|
+
...rows.length === 0 ? {
|
|
5258
|
+
next: {
|
|
5259
|
+
command: "yourskills browse",
|
|
5260
|
+
why: "pick something to install"
|
|
5578
5261
|
}
|
|
5579
|
-
|
|
5580
|
-
|
|
5262
|
+
} : {}
|
|
5263
|
+
};
|
|
5264
|
+
}
|
|
5265
|
+
// src/installed/outdated.ts
|
|
5266
|
+
function driftTag(row) {
|
|
5267
|
+
return lifecycleTag(row.deprecated ? "deprecated" : undefined);
|
|
5268
|
+
}
|
|
5269
|
+
function hasMoved(installed, available) {
|
|
5270
|
+
return !(namesSameCommit(available, installed) || namesSameCommit(installed, available));
|
|
5271
|
+
}
|
|
5272
|
+
function drift(manifest, pins, catalog) {
|
|
5273
|
+
const byExternalId = new Map(catalog.map((row) => [row.externalId, row]));
|
|
5274
|
+
const byName = new Map(catalog.map((row) => [row.name, row]));
|
|
5275
|
+
const moved = [];
|
|
5276
|
+
const unknown = [];
|
|
5277
|
+
const unpinned = [];
|
|
5278
|
+
for (const entry of Object.values(manifest.skills)) {
|
|
5279
|
+
if (entry.unpinned) {
|
|
5280
|
+
unpinned.push(entry.name);
|
|
5281
|
+
continue;
|
|
5581
5282
|
}
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
|
|
5585
|
-
|
|
5283
|
+
const row = byExternalId.get(entry.externalId) ?? byName.get(entry.name);
|
|
5284
|
+
if (!row) {
|
|
5285
|
+
unknown.push(entry.name);
|
|
5286
|
+
continue;
|
|
5287
|
+
}
|
|
5288
|
+
const pin = entry.bundle ? pins.find((p) => p.bundle === entry.bundle && p.skillId === row.id) : undefined;
|
|
5289
|
+
const available = pin?.pinnedSha ?? row.currentSha;
|
|
5290
|
+
if (!hasMoved(entry.ref, available))
|
|
5291
|
+
continue;
|
|
5292
|
+
moved.push({
|
|
5293
|
+
name: entry.name,
|
|
5294
|
+
bundle: entry.bundle ?? null,
|
|
5295
|
+
installed: entry.ref,
|
|
5296
|
+
available,
|
|
5297
|
+
deprecated: pin?.deprecated ?? row.status === "deprecated"
|
|
5586
5298
|
});
|
|
5587
5299
|
}
|
|
5588
|
-
|
|
5589
|
-
|
|
5590
|
-
return { kind: "updated", applied, failed, manifest: nearest.path };
|
|
5300
|
+
moved.sort((a, b) => a.name.localeCompare(b.name));
|
|
5301
|
+
return { moved, unknown, unpinned };
|
|
5591
5302
|
}
|
|
5592
|
-
async function
|
|
5593
|
-
const
|
|
5594
|
-
|
|
5595
|
-
|
|
5596
|
-
|
|
5303
|
+
async function driftNow(client, nearest) {
|
|
5304
|
+
const [pins, catalog] = await Promise.all([
|
|
5305
|
+
client.bundles.pins.query(),
|
|
5306
|
+
client.skills.list.query({})
|
|
5307
|
+
]);
|
|
5308
|
+
return drift(nearest.manifest, pins, catalog);
|
|
5309
|
+
}
|
|
5310
|
+
async function outdatedReport(client, nearest, quiet = false) {
|
|
5311
|
+
let report2;
|
|
5312
|
+
try {
|
|
5313
|
+
report2 = await driftNow(client, nearest);
|
|
5314
|
+
} catch (error) {
|
|
5315
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
5316
|
+
return {
|
|
5317
|
+
title: "outdated",
|
|
5318
|
+
lines: [
|
|
5319
|
+
{ mark: "fail", text: "Could not reach the registry.", note: message }
|
|
5320
|
+
],
|
|
5321
|
+
json: { manifest: nearest.path, error: message },
|
|
5322
|
+
silent: quiet
|
|
5323
|
+
};
|
|
5597
5324
|
}
|
|
5598
|
-
const
|
|
5599
|
-
|
|
5600
|
-
|
|
5325
|
+
const lines = report2.moved.map((row) => ({
|
|
5326
|
+
mark: "pending",
|
|
5327
|
+
text: `${row.name} ${shortRef(row.installed)} -> ${shortRef(row.available)}`,
|
|
5328
|
+
note: [row.bundle ?? "-", driftTag(row)].filter(Boolean).join(" ")
|
|
5601
5329
|
}));
|
|
5602
|
-
|
|
5603
|
-
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
|
|
5607
|
-
|
|
5608
|
-
try {
|
|
5609
|
-
client = await use.connect();
|
|
5610
|
-
nearest = await use.nearestManifest();
|
|
5611
|
-
} catch {
|
|
5612
|
-
return { kind: "quiet" };
|
|
5330
|
+
for (const name of report2.unknown) {
|
|
5331
|
+
lines.push({
|
|
5332
|
+
mark: "fail",
|
|
5333
|
+
text: name,
|
|
5334
|
+
note: "no longer in your registry"
|
|
5335
|
+
});
|
|
5613
5336
|
}
|
|
5614
|
-
|
|
5615
|
-
|
|
5616
|
-
|
|
5617
|
-
|
|
5618
|
-
|
|
5619
|
-
|
|
5620
|
-
return found.signedOut ? { kind: "signed-out" } : { kind: "quiet" };
|
|
5621
|
-
if (found.moved.length === 0)
|
|
5622
|
-
return { kind: "quiet" };
|
|
5623
|
-
if (config.autoUpdate === false) {
|
|
5624
|
-
return { kind: "behind", moved: found.moved, manifest: nearest.path };
|
|
5337
|
+
for (const name of report2.unpinned) {
|
|
5338
|
+
lines.push({
|
|
5339
|
+
mark: "pending",
|
|
5340
|
+
text: name,
|
|
5341
|
+
note: "not in your registry; imported from disk, nothing to compare"
|
|
5342
|
+
});
|
|
5625
5343
|
}
|
|
5626
|
-
|
|
5344
|
+
const current = report2.moved.length === 0 && report2.unknown.length === 0;
|
|
5345
|
+
if (current && !quiet) {
|
|
5346
|
+
lines.push({
|
|
5347
|
+
mark: "ok",
|
|
5348
|
+
text: "Everything is at the ref your registry pins."
|
|
5349
|
+
});
|
|
5350
|
+
}
|
|
5351
|
+
return {
|
|
5352
|
+
title: "outdated",
|
|
5353
|
+
lines,
|
|
5354
|
+
json: {
|
|
5355
|
+
manifest: nearest.path,
|
|
5356
|
+
outdated: report2.moved,
|
|
5357
|
+
unknown: report2.unknown,
|
|
5358
|
+
unpinned: report2.unpinned
|
|
5359
|
+
},
|
|
5360
|
+
silent: quiet && current,
|
|
5361
|
+
...report2.moved.length > 0 ? {
|
|
5362
|
+
next: {
|
|
5363
|
+
command: "yourskills update --all",
|
|
5364
|
+
why: "install what moved"
|
|
5365
|
+
}
|
|
5366
|
+
} : {}
|
|
5367
|
+
};
|
|
5627
5368
|
}
|
|
5628
|
-
|
|
5629
|
-
|
|
5630
|
-
|
|
5631
|
-
|
|
5369
|
+
// src/hooks/emit.ts
|
|
5370
|
+
function describe(outcome) {
|
|
5371
|
+
switch (outcome.kind) {
|
|
5372
|
+
case "quiet":
|
|
5373
|
+
return [];
|
|
5374
|
+
case "signed-out":
|
|
5375
|
+
return [
|
|
5376
|
+
"yourskills: your sign-in has expired, so your skills are no longer kept up to date.",
|
|
5377
|
+
" run: yourskills login"
|
|
5378
|
+
];
|
|
5379
|
+
case "behind": {
|
|
5380
|
+
const lines = [
|
|
5381
|
+
`yourskills: ${count(outcome.moved.length, "skill")} ${outcome.moved.length === 1 ? "has" : "have"} a newer version in your registry (autoUpdate is off)`
|
|
5382
|
+
];
|
|
5383
|
+
for (const row of outcome.moved) {
|
|
5384
|
+
lines.push(` ${row.name} ${shortRef(row.installed)} -> ${shortRef(row.available)}${column(driftTag(row))}`);
|
|
5385
|
+
}
|
|
5386
|
+
lines.push(" run: yourskills update --all");
|
|
5387
|
+
return lines;
|
|
5388
|
+
}
|
|
5389
|
+
case "updated": {
|
|
5390
|
+
const { applied, failed } = outcome;
|
|
5391
|
+
const lines = [
|
|
5392
|
+
applied.length > 0 ? `yourskills: updated ${count(applied.length, "skill")} to what your registry pins; ${applied.length === 1 ? "it is" : "they are"} live in this session.` : `yourskills: could not update ${count(failed.length, "skill")}`
|
|
5393
|
+
];
|
|
5394
|
+
for (const row of applied) {
|
|
5395
|
+
lines.push(` ${row.name} ${row.from} -> ${row.to}`);
|
|
5396
|
+
}
|
|
5397
|
+
for (const row of failed) {
|
|
5398
|
+
lines.push(` ${row.name} failed: ${row.message}`);
|
|
5399
|
+
}
|
|
5400
|
+
if (applied.length > 0)
|
|
5401
|
+
lines.push(` recorded in ${outcome.manifest}`);
|
|
5402
|
+
if (failed.length > 0)
|
|
5403
|
+
lines.push(" run: yourskills doctor");
|
|
5404
|
+
return lines;
|
|
5405
|
+
}
|
|
5406
|
+
}
|
|
5632
5407
|
}
|
|
5633
|
-
|
|
5634
|
-
|
|
5635
|
-
|
|
5636
|
-
|
|
5637
|
-
|
|
5638
|
-
|
|
5639
|
-
|
|
5640
|
-
|
|
5641
|
-
|
|
5642
|
-
|
|
5643
|
-
|
|
5644
|
-
|
|
5645
|
-
|
|
5646
|
-
|
|
5647
|
-
|
|
5648
|
-
|
|
5649
|
-
|
|
5650
|
-
|
|
5651
|
-
|
|
5652
|
-
|
|
5653
|
-
|
|
5654
|
-
|
|
5655
|
-
|
|
5408
|
+
function describeForModel(outcome) {
|
|
5409
|
+
switch (outcome.kind) {
|
|
5410
|
+
case "quiet":
|
|
5411
|
+
return [];
|
|
5412
|
+
case "signed-out":
|
|
5413
|
+
return [
|
|
5414
|
+
"yourskills: the user's session with the skills registry has expired, so installed skills are no longer being updated. The user has been told to run `yourskills login`; it is their call."
|
|
5415
|
+
];
|
|
5416
|
+
case "behind": {
|
|
5417
|
+
const lines = [
|
|
5418
|
+
`yourskills: ${count(outcome.moved.length, "installed skill")} ${outcome.moved.length === 1 ? "is" : "are"} behind the registry. Automatic updates are off; the user has been told and it is their call whether to run \`yourskills update --all\`.`
|
|
5419
|
+
];
|
|
5420
|
+
for (const row of outcome.moved) {
|
|
5421
|
+
lines.push(` ${row.name} installed ${shortRef(row.installed)}, registry pins ${shortRef(row.available)}${column(driftTag(row))}`);
|
|
5422
|
+
}
|
|
5423
|
+
return lines;
|
|
5424
|
+
}
|
|
5425
|
+
case "updated": {
|
|
5426
|
+
const { applied, failed } = outcome;
|
|
5427
|
+
const lines = [];
|
|
5428
|
+
if (applied.length > 0) {
|
|
5429
|
+
lines.push(`yourskills updated ${count(applied.length, "installed skill")} on disk at the start of this session. The contents changed: re-read a skill before relying on what you remember of it.`);
|
|
5430
|
+
for (const row of applied) {
|
|
5431
|
+
lines.push(` ${row.name} ${row.from} -> ${row.to}`);
|
|
5432
|
+
}
|
|
5433
|
+
lines.push(` recorded in ${outcome.manifest}`);
|
|
5434
|
+
}
|
|
5435
|
+
if (failed.length > 0) {
|
|
5436
|
+
lines.push(`yourskills could not update ${count(failed.length, "installed skill")}; the version on disk is unchanged. The user has been told.`);
|
|
5437
|
+
for (const row of failed) {
|
|
5438
|
+
lines.push(` ${row.name} ${row.message}`);
|
|
5439
|
+
}
|
|
5440
|
+
}
|
|
5441
|
+
return lines;
|
|
5442
|
+
}
|
|
5656
5443
|
}
|
|
5657
5444
|
}
|
|
5658
|
-
|
|
5659
|
-
|
|
5660
|
-
|
|
5661
|
-
|
|
5662
|
-
|
|
5663
|
-
|
|
5445
|
+
function describeStale(stale) {
|
|
5446
|
+
if (!stale)
|
|
5447
|
+
return [];
|
|
5448
|
+
return [
|
|
5449
|
+
`yourskills: you are running ${stale.current}; ${stale.latest} is out.`,
|
|
5450
|
+
" run: yourskills upgrade"
|
|
5451
|
+
];
|
|
5452
|
+
}
|
|
5453
|
+
function describeStaleForModel(stale) {
|
|
5454
|
+
if (!stale)
|
|
5455
|
+
return [];
|
|
5456
|
+
return [
|
|
5457
|
+
`yourskills: the installed CLI is ${stale.current} and ${stale.latest} is published. If the user hits a bug in the \`yourskills\` command itself, check whether it is fixed on the newer version before investigating further. Upgrading is their call; they have been told.`
|
|
5458
|
+
];
|
|
5459
|
+
}
|
|
5460
|
+
function count(n, noun) {
|
|
5461
|
+
return `${n} ${noun}${n === 1 ? "" : "s"}`;
|
|
5462
|
+
}
|
|
5463
|
+
function column(text) {
|
|
5464
|
+
return text ? ` ${text}` : "";
|
|
5465
|
+
}
|
|
5466
|
+
function emit(outcome, stale = null) {
|
|
5467
|
+
const lines = [...describe(outcome), ...describeStale(stale)];
|
|
5468
|
+
if (lines.length === 0)
|
|
5664
5469
|
return;
|
|
5665
|
-
const
|
|
5666
|
-
|
|
5470
|
+
const installed = outcome.kind === "updated" && outcome.applied.length > 0;
|
|
5471
|
+
console.log(JSON.stringify({
|
|
5472
|
+
systemMessage: lines.join(`
|
|
5473
|
+
`),
|
|
5474
|
+
hookSpecificOutput: {
|
|
5475
|
+
hookEventName: "SessionStart",
|
|
5476
|
+
additionalContext: [
|
|
5477
|
+
...describeForModel(outcome),
|
|
5478
|
+
...describeStaleForModel(stale)
|
|
5479
|
+
].join(`
|
|
5480
|
+
`),
|
|
5481
|
+
...installed ? { reloadSkills: true } : {}
|
|
5482
|
+
}
|
|
5483
|
+
}));
|
|
5667
5484
|
}
|
|
5668
|
-
|
|
5669
|
-
|
|
5670
|
-
|
|
5671
|
-
const
|
|
5672
|
-
if (
|
|
5673
|
-
return null;
|
|
5674
|
-
const entry = await lookup(name, payload.cwd);
|
|
5675
|
-
if (!entry)
|
|
5485
|
+
// src/hooks/ensure.ts
|
|
5486
|
+
import { existsSync as existsSync4 } from "node:fs";
|
|
5487
|
+
async function registerInto(adapter, tracking) {
|
|
5488
|
+
const wanted2 = adapter.entries.filter((entry) => entry.kind === "update-check" || tracking);
|
|
5489
|
+
if (wanted2.length === 0)
|
|
5676
5490
|
return null;
|
|
5677
|
-
const
|
|
5678
|
-
|
|
5679
|
-
|
|
5680
|
-
|
|
5681
|
-
|
|
5682
|
-
|
|
5683
|
-
|
|
5684
|
-
|
|
5491
|
+
const path = adapter.settingsPath();
|
|
5492
|
+
if (!path)
|
|
5493
|
+
return null;
|
|
5494
|
+
if (!existsSync4(adapter.home()))
|
|
5495
|
+
return null;
|
|
5496
|
+
const applied = await applyTo(path, (settings) => addHooks(settings, wanted2));
|
|
5497
|
+
if (!applied.changed)
|
|
5498
|
+
return null;
|
|
5499
|
+
return { agent: adapter.agent, path, entries: wanted2 };
|
|
5685
5500
|
}
|
|
5686
|
-
|
|
5687
|
-
|
|
5688
|
-
|
|
5689
|
-
|
|
5690
|
-
|
|
5691
|
-
|
|
5692
|
-
|
|
5693
|
-
|
|
5694
|
-
|
|
5501
|
+
function linesFor2(registered, tracking) {
|
|
5502
|
+
const agents = registered.map((r) => r.agent).join(", ");
|
|
5503
|
+
const usage = registered.some((r) => r.entries.some((entry) => entry.kind === "usage"));
|
|
5504
|
+
const lines = [
|
|
5505
|
+
`+ yourskills will check your skills once a day, when ${agents} starts, and update what moved`
|
|
5506
|
+
];
|
|
5507
|
+
if (usage) {
|
|
5508
|
+
lines.push(`+ yourskills will count which of your organization's skills you invoke in ${agents}, so your organization can see what is used`);
|
|
5509
|
+
}
|
|
5510
|
+
for (const { agent, path } of registered) {
|
|
5511
|
+
lines.push(` ${agent} ${path}`);
|
|
5512
|
+
}
|
|
5513
|
+
lines.push(" undo with: yourskills hooks uninstall");
|
|
5514
|
+
if (usage && tracking && !trackingDisabledByEnv()) {
|
|
5515
|
+
lines.push(" turn usage counting off with: yourskills config set track false");
|
|
5516
|
+
}
|
|
5517
|
+
return lines;
|
|
5695
5518
|
}
|
|
5696
|
-
async function
|
|
5697
|
-
|
|
5698
|
-
|
|
5519
|
+
async function ensureHooksRegistered() {
|
|
5520
|
+
try {
|
|
5521
|
+
const config = await readConfig();
|
|
5522
|
+
if (config.hooksOffered)
|
|
5523
|
+
return [];
|
|
5524
|
+
if (!config.token)
|
|
5525
|
+
return [];
|
|
5526
|
+
const tracking = trackingOn(config);
|
|
5527
|
+
const registered = [];
|
|
5528
|
+
for (const adapter of HOOK_ADAPTERS) {
|
|
5529
|
+
try {
|
|
5530
|
+
const result = await registerInto(adapter, tracking);
|
|
5531
|
+
if (result)
|
|
5532
|
+
registered.push(result);
|
|
5533
|
+
} catch {}
|
|
5534
|
+
}
|
|
5535
|
+
await updateConfig((stored) => ({
|
|
5536
|
+
...stored,
|
|
5537
|
+
hooksOffered: new Date().toISOString()
|
|
5538
|
+
}));
|
|
5539
|
+
return registered.length > 0 ? linesFor2(registered, tracking) : [];
|
|
5540
|
+
} catch {
|
|
5541
|
+
return [];
|
|
5542
|
+
}
|
|
5699
5543
|
}
|
|
5700
5544
|
// src/upgrade/upgrade.ts
|
|
5701
5545
|
import { spawn as spawn3 } from "node:child_process";
|
|
@@ -5703,7 +5547,7 @@ import { realpathSync } from "node:fs";
|
|
|
5703
5547
|
// package.json
|
|
5704
5548
|
var package_default = {
|
|
5705
5549
|
name: "yourskills",
|
|
5706
|
-
version: "0.1.
|
|
5550
|
+
version: "0.1.5",
|
|
5707
5551
|
description: "Browse, install and keep up to date the agent skills and bundles your team publishes on yourskills.",
|
|
5708
5552
|
license: "UNLICENSED",
|
|
5709
5553
|
homepage: "https://yourskills.store",
|
|
@@ -5762,7 +5606,7 @@ var VERSION = package_default.version ?? "0.0.0-dev";
|
|
|
5762
5606
|
// src/upgrade/upgrade.ts
|
|
5763
5607
|
var PACKAGE = "yourskills";
|
|
5764
5608
|
var REGISTRY = `https://registry.npmjs.org/${PACKAGE}/latest`;
|
|
5765
|
-
var
|
|
5609
|
+
var DAY_MS = 24 * 60 * 60 * 1000;
|
|
5766
5610
|
function installMethod(entry = process.argv[1], env = process.env) {
|
|
5767
5611
|
let path = entry ?? "";
|
|
5768
5612
|
try {
|
|
@@ -5921,33 +5765,232 @@ async function remember(latest) {
|
|
|
5921
5765
|
}));
|
|
5922
5766
|
} catch {}
|
|
5923
5767
|
}
|
|
5924
|
-
|
|
5768
|
+
var ASK_TIMEOUT_MS = 1500;
|
|
5769
|
+
async function upgradeCheck({
|
|
5770
|
+
now = new Date,
|
|
5771
|
+
current = VERSION,
|
|
5772
|
+
ask = latestVersion
|
|
5773
|
+
} = {}) {
|
|
5925
5774
|
if (process.env.YOURSKILLS_NO_UPGRADE_CHECK === "1")
|
|
5926
5775
|
return null;
|
|
5927
5776
|
try {
|
|
5928
5777
|
const config = await readConfig();
|
|
5929
5778
|
const last = config.lastUpgradeCheck ? Date.parse(config.lastUpgradeCheck) : 0;
|
|
5930
5779
|
let latest = config.latestVersion ?? null;
|
|
5931
|
-
if (Number.isNaN(last) || now.getTime() - last >=
|
|
5780
|
+
if (Number.isNaN(last) || now.getTime() - last >= DAY_MS) {
|
|
5932
5781
|
await updateConfig((stored) => ({
|
|
5933
5782
|
...stored,
|
|
5934
5783
|
lastUpgradeCheck: now.toISOString()
|
|
5935
5784
|
}));
|
|
5936
|
-
|
|
5937
|
-
|
|
5938
|
-
|
|
5785
|
+
const answered = await ask(ASK_TIMEOUT_MS);
|
|
5786
|
+
if (answered) {
|
|
5787
|
+
latest = answered;
|
|
5939
5788
|
await updateConfig((stored) => ({
|
|
5940
5789
|
...stored,
|
|
5941
5790
|
latestVersion: answered
|
|
5942
5791
|
}));
|
|
5792
|
+
}
|
|
5943
5793
|
}
|
|
5944
|
-
if (!latest || !isNewer(latest,
|
|
5794
|
+
if (!latest || !isNewer(latest, current))
|
|
5945
5795
|
return null;
|
|
5946
|
-
return
|
|
5796
|
+
return { current, latest };
|
|
5947
5797
|
} catch {
|
|
5948
5798
|
return null;
|
|
5949
5799
|
}
|
|
5950
5800
|
}
|
|
5801
|
+
async function upgradeNotice(now = new Date) {
|
|
5802
|
+
const stale = await upgradeCheck({ now });
|
|
5803
|
+
if (!stale)
|
|
5804
|
+
return null;
|
|
5805
|
+
return `+ yourskills ${stale.latest} is out; you have ${stale.current}. run: yourskills upgrade`;
|
|
5806
|
+
}
|
|
5807
|
+
// src/hooks/session.ts
|
|
5808
|
+
var CHECK_BUDGET_MS = 5000;
|
|
5809
|
+
var UPDATE_BUDGET_MS = 90000;
|
|
5810
|
+
var DAY_MS2 = 86400000;
|
|
5811
|
+
var REAL = {
|
|
5812
|
+
now: () => Date.now(),
|
|
5813
|
+
readConfig,
|
|
5814
|
+
updateConfig,
|
|
5815
|
+
connect: () => createClient(),
|
|
5816
|
+
nearestManifest: () => nearestManifest(),
|
|
5817
|
+
driftNow,
|
|
5818
|
+
add,
|
|
5819
|
+
checkBudgetMs: CHECK_BUDGET_MS,
|
|
5820
|
+
resolveAgents: () => resolveAgents(),
|
|
5821
|
+
landed: listInstalled
|
|
5822
|
+
};
|
|
5823
|
+
function updateCheckDue(last, now = Date.now()) {
|
|
5824
|
+
if (!last)
|
|
5825
|
+
return true;
|
|
5826
|
+
const at = Date.parse(last);
|
|
5827
|
+
return Number.isNaN(at) || now - at >= DAY_MS2;
|
|
5828
|
+
}
|
|
5829
|
+
function withBudget(work, ms) {
|
|
5830
|
+
return Promise.race([
|
|
5831
|
+
work,
|
|
5832
|
+
new Promise((resolve3) => {
|
|
5833
|
+
const timer = setTimeout(() => resolve3(undefined), ms);
|
|
5834
|
+
timer.unref?.();
|
|
5835
|
+
})
|
|
5836
|
+
]);
|
|
5837
|
+
}
|
|
5838
|
+
async function check(ports, client, nearest) {
|
|
5839
|
+
try {
|
|
5840
|
+
const report2 = await ports.driftNow(client, nearest);
|
|
5841
|
+
return { ok: true, moved: report2.moved };
|
|
5842
|
+
} catch (error) {
|
|
5843
|
+
return { ok: false, signedOut: isUnauthorized(error) };
|
|
5844
|
+
}
|
|
5845
|
+
}
|
|
5846
|
+
async function applyUpdates(ports, client, nearest, moved, deadline) {
|
|
5847
|
+
const where2 = {
|
|
5848
|
+
...nearest,
|
|
5849
|
+
agents: await ports.resolveAgents(),
|
|
5850
|
+
landed: ports.landed
|
|
5851
|
+
};
|
|
5852
|
+
const wanted2 = new Map(moved.map((row) => [row.name, row]));
|
|
5853
|
+
const applied = [];
|
|
5854
|
+
const failed = [];
|
|
5855
|
+
const names = [
|
|
5856
|
+
...new Set(moved.map((row) => row.bundle).filter((name) => !!name))
|
|
5857
|
+
].sort().map((bundle) => `bundle:${bundle}`).concat(moved.filter((row) => !row.bundle).map((row) => row.name).sort());
|
|
5858
|
+
try {
|
|
5859
|
+
for await (const event of ports.add(names, where2, async () => client).stream()) {
|
|
5860
|
+
if (event.kind === "installed") {
|
|
5861
|
+
const row = wanted2.get(event.location.skillName);
|
|
5862
|
+
if (row) {
|
|
5863
|
+
applied.push({
|
|
5864
|
+
name: row.name,
|
|
5865
|
+
from: shortRef(row.installed),
|
|
5866
|
+
to: shortRef(row.available)
|
|
5867
|
+
});
|
|
5868
|
+
}
|
|
5869
|
+
} else if (event.kind === "failed" && wanted2.has(event.name)) {
|
|
5870
|
+
failed.push({ name: event.name, message: event.message });
|
|
5871
|
+
}
|
|
5872
|
+
if (ports.now() > deadline)
|
|
5873
|
+
break;
|
|
5874
|
+
}
|
|
5875
|
+
} catch (error) {
|
|
5876
|
+
failed.push({
|
|
5877
|
+
name: names.join(", "),
|
|
5878
|
+
message: error instanceof Error ? error.message : String(error)
|
|
5879
|
+
});
|
|
5880
|
+
}
|
|
5881
|
+
if (applied.length === 0 && failed.length === 0)
|
|
5882
|
+
return { kind: "quiet" };
|
|
5883
|
+
return { kind: "updated", applied, failed, manifest: nearest.path };
|
|
5884
|
+
}
|
|
5885
|
+
async function sessionStart(ports = {}) {
|
|
5886
|
+
const use = { ...REAL, ...ports };
|
|
5887
|
+
const config = await use.readConfig();
|
|
5888
|
+
if (!updateCheckDue(config.lastUpdateCheck, use.now())) {
|
|
5889
|
+
return { kind: "quiet" };
|
|
5890
|
+
}
|
|
5891
|
+
const written = await use.updateConfig((current) => ({
|
|
5892
|
+
...current,
|
|
5893
|
+
lastUpdateCheck: new Date(use.now()).toISOString()
|
|
5894
|
+
}));
|
|
5895
|
+
if (written === null)
|
|
5896
|
+
return { kind: "quiet" };
|
|
5897
|
+
if (!config.token)
|
|
5898
|
+
return { kind: "quiet" };
|
|
5899
|
+
let client;
|
|
5900
|
+
let nearest;
|
|
5901
|
+
try {
|
|
5902
|
+
client = await use.connect();
|
|
5903
|
+
nearest = await use.nearestManifest();
|
|
5904
|
+
} catch {
|
|
5905
|
+
return { kind: "quiet" };
|
|
5906
|
+
}
|
|
5907
|
+
if (Object.keys(nearest.manifest.skills).length === 0)
|
|
5908
|
+
return { kind: "quiet" };
|
|
5909
|
+
const found = await withBudget(check(use, client, nearest), use.checkBudgetMs);
|
|
5910
|
+
if (!found)
|
|
5911
|
+
return { kind: "quiet" };
|
|
5912
|
+
if (!found.ok)
|
|
5913
|
+
return found.signedOut ? { kind: "signed-out" } : { kind: "quiet" };
|
|
5914
|
+
if (found.moved.length === 0)
|
|
5915
|
+
return { kind: "quiet" };
|
|
5916
|
+
if (config.autoUpdate === false) {
|
|
5917
|
+
return { kind: "behind", moved: found.moved, manifest: nearest.path };
|
|
5918
|
+
}
|
|
5919
|
+
return applyUpdates(use, client, nearest, found.moved, use.now() + UPDATE_BUDGET_MS);
|
|
5920
|
+
}
|
|
5921
|
+
|
|
5922
|
+
// src/hooks/run.ts
|
|
5923
|
+
function isHookEventName(value) {
|
|
5924
|
+
return value === "session-start" || value === "post-tool-use";
|
|
5925
|
+
}
|
|
5926
|
+
var STDIN_BUDGET_MS = 2000;
|
|
5927
|
+
async function readStdin() {
|
|
5928
|
+
if (process.stdin.isTTY)
|
|
5929
|
+
return {};
|
|
5930
|
+
const text = await Promise.race([
|
|
5931
|
+
(async () => {
|
|
5932
|
+
let raw = "";
|
|
5933
|
+
process.stdin.setEncoding("utf8");
|
|
5934
|
+
for await (const chunk of process.stdin)
|
|
5935
|
+
raw += chunk;
|
|
5936
|
+
return raw;
|
|
5937
|
+
})(),
|
|
5938
|
+
new Promise((resolve3) => {
|
|
5939
|
+
const timer = setTimeout(() => resolve3(""), STDIN_BUDGET_MS);
|
|
5940
|
+
timer.unref?.();
|
|
5941
|
+
})
|
|
5942
|
+
]);
|
|
5943
|
+
if (text.trim().length === 0)
|
|
5944
|
+
return {};
|
|
5945
|
+
try {
|
|
5946
|
+
return JSON.parse(text);
|
|
5947
|
+
} catch {
|
|
5948
|
+
return {};
|
|
5949
|
+
}
|
|
5950
|
+
}
|
|
5951
|
+
async function lookup(name, cwd) {
|
|
5952
|
+
const nearest = await nearestManifest(undefined, cwd);
|
|
5953
|
+
const found = nearest.manifest.skills[name];
|
|
5954
|
+
if (found)
|
|
5955
|
+
return found;
|
|
5956
|
+
if (nearest.scope === "global")
|
|
5957
|
+
return;
|
|
5958
|
+
const global = readManifest(manifestPathFor("global"));
|
|
5959
|
+
return global.skills[name];
|
|
5960
|
+
}
|
|
5961
|
+
async function queueInvocation(payload, now = new Date) {
|
|
5962
|
+
if (!await trackingEnabled())
|
|
5963
|
+
return null;
|
|
5964
|
+
const name = payload.tool_input?.skill;
|
|
5965
|
+
if (typeof name !== "string" || name.length === 0)
|
|
5966
|
+
return null;
|
|
5967
|
+
const entry = await lookup(name, payload.cwd);
|
|
5968
|
+
if (!entry)
|
|
5969
|
+
return null;
|
|
5970
|
+
const event = {
|
|
5971
|
+
skillExternalId: entry.externalId,
|
|
5972
|
+
ref: entry.ref,
|
|
5973
|
+
agent: agentForEvent("usage", "PostToolUse"),
|
|
5974
|
+
occurredAt: now.toISOString()
|
|
5975
|
+
};
|
|
5976
|
+
await appendEvent(event);
|
|
5977
|
+
return event;
|
|
5978
|
+
}
|
|
5979
|
+
async function runHook(event) {
|
|
5980
|
+
try {
|
|
5981
|
+
const payload = await readStdin();
|
|
5982
|
+
if (event === "post-tool-use")
|
|
5983
|
+
await queueInvocation(payload);
|
|
5984
|
+
else
|
|
5985
|
+
await runSessionStart();
|
|
5986
|
+
} catch {}
|
|
5987
|
+
process.exitCode = 0;
|
|
5988
|
+
}
|
|
5989
|
+
async function runSessionStart() {
|
|
5990
|
+
await flushQuietly();
|
|
5991
|
+
const outcome = await sessionStart();
|
|
5992
|
+
emit(outcome, await upgradeCheck());
|
|
5993
|
+
}
|
|
5951
5994
|
// src/doctor/doctor.ts
|
|
5952
5995
|
var REACH_TIMEOUT_MS = 5000;
|
|
5953
5996
|
function withLogin(message) {
|
|
@@ -6281,7 +6324,7 @@ function trackFrom(config) {
|
|
|
6281
6324
|
return ["off", "YOURSKILLS_NO_TRACK in the environment"];
|
|
6282
6325
|
if (config.track !== undefined)
|
|
6283
6326
|
return [config.track ? "on" : "off", `track in ${configPath()}`];
|
|
6284
|
-
return ["
|
|
6327
|
+
return ["on", "built-in default: turn off with `config set track false`"];
|
|
6285
6328
|
}
|
|
6286
6329
|
var LABEL = 12;
|
|
6287
6330
|
function row(label, value, from) {
|
|
@@ -7101,7 +7144,7 @@ var TABLE = {
|
|
|
7101
7144
|
},
|
|
7102
7145
|
{
|
|
7103
7146
|
usage: "hooks install",
|
|
7104
|
-
blurb: "Register them: the session update, and usage
|
|
7147
|
+
blurb: "Register them: the session update, and usage unless turned off"
|
|
7105
7148
|
},
|
|
7106
7149
|
{
|
|
7107
7150
|
usage: "hooks uninstall",
|
|
@@ -8031,7 +8074,18 @@ function AccountLine({ facts, width }) {
|
|
|
8031
8074
|
]
|
|
8032
8075
|
});
|
|
8033
8076
|
}
|
|
8077
|
+
function StaleLine({ stale, width }) {
|
|
8078
|
+
const text = `${mark.ok} ${stale.latest} is out; you have ${stale.current}. type upgrade`;
|
|
8079
|
+
return /* @__PURE__ */ jsx18(Box14, {
|
|
8080
|
+
width: width - FRAME_CHROME,
|
|
8081
|
+
children: /* @__PURE__ */ jsx18(Text16, {
|
|
8082
|
+
color: tone.notice,
|
|
8083
|
+
children: clip(text, width - FRAME_CHROME)
|
|
8084
|
+
})
|
|
8085
|
+
});
|
|
8086
|
+
}
|
|
8034
8087
|
var RUN = "run";
|
|
8088
|
+
var ASK_NPM = () => upgradeCheck();
|
|
8035
8089
|
var NO_EXIT_CODE = () => {
|
|
8036
8090
|
return;
|
|
8037
8091
|
};
|
|
@@ -8046,7 +8100,8 @@ function HomeScreen({
|
|
|
8046
8100
|
onSummary,
|
|
8047
8101
|
onWelcomed = async () => {
|
|
8048
8102
|
return;
|
|
8049
|
-
}
|
|
8103
|
+
},
|
|
8104
|
+
checkUpgrade = ASK_NPM
|
|
8050
8105
|
}) {
|
|
8051
8106
|
const { exit } = useApp7();
|
|
8052
8107
|
const width = useFrameWidth();
|
|
@@ -8058,6 +8113,19 @@ function HomeScreen({
|
|
|
8058
8113
|
const [route, setRoute] = useState11(null);
|
|
8059
8114
|
const [welcome, setWelcome] = useState11(initial.firstRun);
|
|
8060
8115
|
const [wizard, setWizard] = useState11(false);
|
|
8116
|
+
const [stale, setStale] = useState11(null);
|
|
8117
|
+
useEffect9(() => {
|
|
8118
|
+
let live = true;
|
|
8119
|
+
checkUpgrade().then((found) => {
|
|
8120
|
+
if (live)
|
|
8121
|
+
setStale(found);
|
|
8122
|
+
}).catch(() => {
|
|
8123
|
+
return;
|
|
8124
|
+
});
|
|
8125
|
+
return () => {
|
|
8126
|
+
live = false;
|
|
8127
|
+
};
|
|
8128
|
+
}, [checkUpgrade]);
|
|
8061
8129
|
const typed = useMemo4(() => typedCommand(query), [query]);
|
|
8062
8130
|
const visible = useMemo4(() => {
|
|
8063
8131
|
const filtered = filterCommands(groups, query);
|
|
@@ -8406,6 +8474,10 @@ function HomeScreen({
|
|
|
8406
8474
|
facts,
|
|
8407
8475
|
width
|
|
8408
8476
|
}),
|
|
8477
|
+
stale ? /* @__PURE__ */ jsx18(StaleLine, {
|
|
8478
|
+
stale,
|
|
8479
|
+
width
|
|
8480
|
+
}) : null,
|
|
8409
8481
|
/* @__PURE__ */ jsx18(Divider, {
|
|
8410
8482
|
width
|
|
8411
8483
|
}),
|