skillwatch 0.1.0 → 0.1.1
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/LICENSE.md +1 -1
- package/README.md +5 -78
- package/dist/checker.js +0 -2
- package/dist/cli.js +40 -14
- package/package.json +8 -6
- package/dist/checker.js.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/com.mblode.skillwatch.plist.template +0 -28
- package/dist/package-D8pw1JVM.js +0 -59
- package/dist/package-D8pw1JVM.js.map +0 -1
package/LICENSE.md
CHANGED
package/README.md
CHANGED
|
@@ -1,16 +1,6 @@
|
|
|
1
1
|
# skillwatch
|
|
2
2
|
|
|
3
|
-
Daily macOS notifications when installed
|
|
4
|
-
|
|
5
|
-
- Compares each installed skill's `skillFolderHash` with the current GitHub tree hash for that folder
|
|
6
|
-
- Installs a per-user LaunchAgent that runs once per day
|
|
7
|
-
- Deduplicates notifications until the update set changes
|
|
8
|
-
|
|
9
|
-
## Requirements
|
|
10
|
-
|
|
11
|
-
- macOS
|
|
12
|
-
- Node.js 22 or newer
|
|
13
|
-
- An existing skills lock file at `~/.agents/.skill-lock.json` or `$XDG_STATE_HOME/skills/.skill-lock.json`
|
|
3
|
+
Daily macOS notifications when your installed agent skills have updates on GitHub.
|
|
14
4
|
|
|
15
5
|
## Install
|
|
16
6
|
|
|
@@ -18,14 +8,9 @@ Daily macOS notifications when installed GitHub-backed skills have updates.
|
|
|
18
8
|
npx skillwatch install
|
|
19
9
|
```
|
|
20
10
|
|
|
21
|
-
|
|
11
|
+
Runs daily at 09:00. Customise with `--hour 14 --minute 30`.
|
|
22
12
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
```bash
|
|
26
|
-
npm install -g skillwatch
|
|
27
|
-
skillwatch install
|
|
28
|
-
```
|
|
13
|
+
For frequent use, install globally with `npm install -g skillwatch`.
|
|
29
14
|
|
|
30
15
|
## Verify
|
|
31
16
|
|
|
@@ -39,68 +24,10 @@ npx skillwatch check-now
|
|
|
39
24
|
npx skillwatch uninstall
|
|
40
25
|
```
|
|
41
26
|
|
|
42
|
-
If you installed the CLI globally and want to remove that too:
|
|
43
|
-
|
|
44
|
-
```bash
|
|
45
|
-
npm uninstall -g skillwatch
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
This removes the LaunchAgent, installed support files, state, and logs. The global uninstall only removes the CLI package.
|
|
49
|
-
|
|
50
|
-
## Custom Time
|
|
51
|
-
|
|
52
|
-
```bash
|
|
53
|
-
npx skillwatch install --hour 14 --minute 30
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
## What Gets Installed
|
|
57
|
-
|
|
58
|
-
- LaunchAgent: `~/Library/LaunchAgents/com.mblode.skillwatch.plist`
|
|
59
|
-
- Checker: `~/Library/Application Support/skillwatch/checker.js`
|
|
60
|
-
- State: `~/Library/Application Support/skillwatch/state.json`
|
|
61
|
-
- Logs: `~/Library/Logs/skillwatch/`
|
|
62
|
-
|
|
63
27
|
## Troubleshooting
|
|
64
28
|
|
|
65
|
-
-
|
|
66
|
-
-
|
|
67
|
-
- This project is macOS-only and requires `launchctl`, `plutil`, and `osascript`.
|
|
68
|
-
|
|
69
|
-
## Local Development
|
|
70
|
-
|
|
71
|
-
Use the repo wrapper if you are working from source:
|
|
72
|
-
|
|
73
|
-
```bash
|
|
74
|
-
git clone https://github.com/mblode/update-skills.git
|
|
75
|
-
cd update-skills
|
|
76
|
-
npm install
|
|
77
|
-
./install.sh
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
`./install.sh` auto-builds `dist/` if needed and forwards to the built CLI.
|
|
81
|
-
|
|
82
|
-
Use the built CLI directly if you want to test the package output:
|
|
83
|
-
|
|
84
|
-
```bash
|
|
85
|
-
npm run build
|
|
86
|
-
node dist/cli.js install
|
|
87
|
-
node dist/cli.js check-now
|
|
88
|
-
node dist/cli.js uninstall
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
Install command options from `--help`:
|
|
92
|
-
|
|
93
|
-
```text
|
|
94
|
-
--hour <0-23> Daily check hour, default 9
|
|
95
|
-
--minute <0-59> Daily check minute, default 0
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
## Development
|
|
99
|
-
|
|
100
|
-
```bash
|
|
101
|
-
npm run validate
|
|
102
|
-
npm run pack:dry-run
|
|
103
|
-
```
|
|
29
|
+
- **"No skill lock file found"** — run `npx skills` first so the lock file exists.
|
|
30
|
+
- **Node path changed** — rerun `npx skillwatch install` to update the LaunchAgent.
|
|
104
31
|
|
|
105
32
|
## License
|
|
106
33
|
|
package/dist/checker.js
CHANGED
package/dist/cli.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { execFileSync } from "node:child_process";
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
|
-
import { copyFile, mkdir,
|
|
3
|
+
import { copyFile, mkdir, rm, writeFile } from "node:fs/promises";
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
5
|
import { dirname, join, resolve } from "node:path";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
7
|
//#region src/cli.ts
|
|
8
|
-
const
|
|
8
|
+
const PACKAGE_NAME = "skillwatch";
|
|
9
|
+
const PACKAGE_VERSION = "0.1.1";
|
|
9
10
|
const ENTRYPOINT_PATH = fileURLToPath(import.meta.url);
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const COMMAND_NAME = name.split("/").at(-1) ?? "skillwatch";
|
|
11
|
+
const CHECKER_SOURCE_PATH = join(dirname(ENTRYPOINT_PATH), "checker.js");
|
|
12
|
+
const COMMAND_NAME = PACKAGE_NAME.split("/").at(-1) ?? "skillwatch";
|
|
13
13
|
const APP_DIR_NAME = "skillwatch";
|
|
14
14
|
const LEGACY_APP_DIR_NAME = "skills-update-notifier";
|
|
15
15
|
const fail = (message) => {
|
|
@@ -44,9 +44,38 @@ const getUid = () => {
|
|
|
44
44
|
if (typeof process.getuid !== "function") fail("Could not determine the current user id.");
|
|
45
45
|
return process.getuid();
|
|
46
46
|
};
|
|
47
|
-
const
|
|
47
|
+
const renderPlist = (options, paths, nodePath = process.execPath) => `<?xml version="1.0" encoding="UTF-8"?>
|
|
48
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
49
|
+
<plist version="1.0">
|
|
50
|
+
<dict>
|
|
51
|
+
<key>Label</key>
|
|
52
|
+
<string>${LABEL}</string>
|
|
53
|
+
|
|
54
|
+
<key>ProgramArguments</key>
|
|
55
|
+
<array>
|
|
56
|
+
<string>${nodePath}</string>
|
|
57
|
+
<string>${paths.checkerTargetPath}</string>
|
|
58
|
+
</array>
|
|
59
|
+
|
|
60
|
+
<key>StartCalendarInterval</key>
|
|
61
|
+
<dict>
|
|
62
|
+
<key>Hour</key>
|
|
63
|
+
<integer>${options.hour}</integer>
|
|
64
|
+
<key>Minute</key>
|
|
65
|
+
<integer>${options.minute}</integer>
|
|
66
|
+
</dict>
|
|
67
|
+
|
|
68
|
+
<key>StandardOutPath</key>
|
|
69
|
+
<string>${paths.stdoutPath}</string>
|
|
70
|
+
|
|
71
|
+
<key>StandardErrorPath</key>
|
|
72
|
+
<string>${paths.stderrPath}</string>
|
|
73
|
+
</dict>
|
|
74
|
+
</plist>
|
|
75
|
+
`;
|
|
76
|
+
const writePlist = async (options, paths) => {
|
|
48
77
|
const plistTargetPath = getPlistTargetPath();
|
|
49
|
-
const content = (
|
|
78
|
+
const content = renderPlist(options, paths);
|
|
50
79
|
await mkdir(dirname(plistTargetPath), { recursive: true });
|
|
51
80
|
await writeFile(plistTargetPath, content, "utf8");
|
|
52
81
|
};
|
|
@@ -67,7 +96,7 @@ const bootstrapAgent = () => {
|
|
|
67
96
|
};
|
|
68
97
|
const printUsage = () => {
|
|
69
98
|
console.log(`
|
|
70
|
-
${
|
|
99
|
+
${PACKAGE_NAME} ${PACKAGE_VERSION}
|
|
71
100
|
|
|
72
101
|
Usage:
|
|
73
102
|
${COMMAND_NAME} install [--hour 9] [--minute 0]
|
|
@@ -119,11 +148,10 @@ const installCommand = async (args) => {
|
|
|
119
148
|
const appDir = getAppDir();
|
|
120
149
|
const logDir = getLogDir();
|
|
121
150
|
const checkerTargetPath = join(appDir, "checker.js");
|
|
122
|
-
const plistTemplatePath = join(PACKAGE_DIR, "com.mblode.skillwatch.plist.template");
|
|
123
151
|
await mkdir(appDir, { recursive: true });
|
|
124
152
|
await mkdir(logDir, { recursive: true });
|
|
125
153
|
await copyFile(CHECKER_SOURCE_PATH, checkerTargetPath);
|
|
126
|
-
await writePlist(
|
|
154
|
+
await writePlist(options, {
|
|
127
155
|
checkerTargetPath,
|
|
128
156
|
stderrPath: join(logDir, "stderr.log"),
|
|
129
157
|
stdoutPath: join(logDir, "stdout.log")
|
|
@@ -186,7 +214,7 @@ const main = async () => {
|
|
|
186
214
|
return;
|
|
187
215
|
}
|
|
188
216
|
if (command === "--version" || command === "-v" || command === "version") {
|
|
189
|
-
console.log(
|
|
217
|
+
console.log(PACKAGE_VERSION);
|
|
190
218
|
return;
|
|
191
219
|
}
|
|
192
220
|
if (command === "install") {
|
|
@@ -213,6 +241,4 @@ if (isExecutedDirectly()) try {
|
|
|
213
241
|
fail(error instanceof Error ? error.stack || error.message : String(error));
|
|
214
242
|
}
|
|
215
243
|
//#endregion
|
|
216
|
-
export { parseInteger };
|
|
217
|
-
|
|
218
|
-
//# sourceMappingURL=cli.js.map
|
|
244
|
+
export { parseInteger, renderPlist };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skillwatch",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Daily macOS notifications when installed GitHub-backed skills have updates.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent-skills",
|
|
@@ -9,17 +9,19 @@
|
|
|
9
9
|
"notification",
|
|
10
10
|
"skills"
|
|
11
11
|
],
|
|
12
|
-
"homepage": "https://github.com/mblode/
|
|
12
|
+
"homepage": "https://github.com/mblode/skillwatch#readme",
|
|
13
13
|
"bugs": {
|
|
14
|
-
"url": "https://github.com/mblode/
|
|
14
|
+
"url": "https://github.com/mblode/skillwatch/issues"
|
|
15
15
|
},
|
|
16
16
|
"license": "MIT",
|
|
17
17
|
"author": "Matt Blode",
|
|
18
18
|
"repository": {
|
|
19
19
|
"type": "git",
|
|
20
|
-
"url": "git+https://github.com/mblode/
|
|
20
|
+
"url": "git+https://github.com/mblode/skillwatch.git"
|
|
21
|
+
},
|
|
22
|
+
"bin": {
|
|
23
|
+
"skillwatch": "./dist/cli.js"
|
|
21
24
|
},
|
|
22
|
-
"bin": "./dist/cli.js",
|
|
23
25
|
"files": [
|
|
24
26
|
"dist",
|
|
25
27
|
"README.md",
|
|
@@ -30,7 +32,7 @@
|
|
|
30
32
|
"access": "public"
|
|
31
33
|
},
|
|
32
34
|
"scripts": {
|
|
33
|
-
"build": "tsdown
|
|
35
|
+
"build": "tsdown",
|
|
34
36
|
"dev": "tsdown --watch",
|
|
35
37
|
"typecheck": "tsc --noEmit",
|
|
36
38
|
"check": "ultracite check",
|
package/dist/checker.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"checker.js","names":[],"sources":["../src/checker.ts"],"sourcesContent":["import { execFileSync } from \"node:child_process\";\nimport { createHash } from \"node:crypto\";\nimport { existsSync } from \"node:fs\";\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\n// --- Types ---\n\ninterface LockFileEntry {\n sourceType?: string;\n source?: string;\n sourceUrl?: string;\n skillPath?: string;\n skillFolderHash?: string;\n}\n\ninterface LockFile {\n skills?: Record<string, LockFileEntry>;\n}\n\ninterface TrackedSkill {\n skillName: string;\n repoId: string;\n skillPath: string;\n localHash: string;\n}\n\ninterface UpdateResult {\n repoId: string;\n skillName: string;\n localHash: string;\n remoteHash: string;\n}\n\ninterface CheckError {\n repoId: string;\n skillName: string;\n reason: string;\n}\n\ninterface CheckResult {\n trackedSkills: TrackedSkill[];\n updates: UpdateResult[];\n errors: CheckError[];\n}\n\ninterface GroupedUpdate {\n repoId: string;\n skillNames: string[];\n}\n\ninterface GitHubTreeEntry {\n path: string;\n type: string;\n sha: string;\n}\n\ninterface GitHubTreeResponse {\n sha?: string;\n tree?: GitHubTreeEntry[];\n}\n\ninterface GitHubRepoResponse {\n default_branch?: string;\n}\n\n// --- Utilities ---\n\nconst log = (message: string): void => {\n const timestamp = new Date().toISOString();\n console.log(`[${timestamp}] ${message}`);\n};\n\nconst readJson = async <T>(path: string, fallback: T): Promise<T> => {\n try {\n const content = await readFile(path, \"utf8\");\n return JSON.parse(content) as T;\n } catch {\n return fallback;\n }\n};\n\nconst writeJson = async (path: string, value: unknown): Promise<void> => {\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, `${JSON.stringify(value, null, 2)}\\n`, \"utf8\");\n};\n\nconst normalizeSkillFolderPath = (skillPath: string): string =>\n skillPath\n .replaceAll(\"\\\\\", \"/\")\n .replace(/\\/?SKILL\\.md$/, \"\")\n .replace(/\\/$/, \"\");\n\n// --- Config ---\n\nconst DEFAULT_STATE_DIR = join(\n homedir(),\n \"Library\",\n \"Application Support\",\n \"skillwatch\"\n);\nconst ENTRYPOINT_PATH = fileURLToPath(import.meta.url);\nconst DEFAULT_STATE_PATH = join(DEFAULT_STATE_DIR, \"state.json\");\nconst DEFAULT_LOCK_PATH = process.env.XDG_STATE_HOME\n ? join(process.env.XDG_STATE_HOME, \"skills\", \".skill-lock.json\")\n : join(homedir(), \".agents\", \".skill-lock.json\");\n\nconst STATE_PATH = process.env.SKILLS_CHECK_STATE_PATH || DEFAULT_STATE_PATH;\nconst LOCK_PATH = process.env.SKILLS_CHECK_LOCK_PATH || DEFAULT_LOCK_PATH;\n\n// --- GitHub API helpers ---\n\nconst getRepoId = (entry: LockFileEntry): string | null => {\n if (typeof entry?.source === \"string\" && entry.source.includes(\"/\")) {\n return entry.source.replace(/\\.git$/, \"\");\n }\n\n if (typeof entry?.sourceUrl === \"string\") {\n const sshMatch = entry.sourceUrl.match(/^git@[^:]+:(.+?)(?:\\.git)?$/);\n if (sshMatch?.[1]) {\n return sshMatch[1];\n }\n\n try {\n const pathname = new URL(entry.sourceUrl).pathname\n .replace(/^\\/+/, \"\")\n .replace(/\\.git$/, \"\");\n if (pathname.includes(\"/\")) {\n return pathname;\n }\n } catch {\n // Ignore parse failures and fall through.\n }\n }\n\n return null;\n};\n\nconst getGitHubToken = (): string | null => {\n const envToken = process.env.GITHUB_TOKEN ?? process.env.GH_TOKEN;\n if (envToken) {\n return envToken;\n }\n\n const ghCandidates = [\n process.env.GH_PATH,\n \"/opt/homebrew/bin/gh\",\n \"/usr/local/bin/gh\",\n \"gh\",\n ].filter(Boolean) as string[];\n\n for (const candidate of ghCandidates) {\n try {\n const token = execFileSync(candidate, [\"auth\", \"token\"], {\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n }).trim();\n\n if (token) {\n return token;\n }\n } catch {\n // Try the next candidate.\n }\n }\n\n return null;\n};\n\nconst fetchGitHubJson = async (\n url: string,\n token: string | null\n): Promise<unknown> => {\n try {\n const response = await fetch(url, {\n headers: {\n Accept: \"application/vnd.github.v3+json\",\n \"User-Agent\": \"skillwatch\",\n ...(token ? { Authorization: `Bearer ${token}` } : {}),\n },\n });\n\n if (!response.ok) {\n return null;\n }\n\n return await response.json();\n } catch {\n return null;\n }\n};\n\nconst fetchRepoTree = async (\n ownerRepo: string,\n token: string | null\n): Promise<GitHubTreeResponse | null> => {\n const repoData = (await fetchGitHubJson(\n `https://api.github.com/repos/${ownerRepo}`,\n token\n )) as GitHubRepoResponse | null;\n\n const branches = [repoData?.default_branch, \"main\", \"master\"].filter(\n Boolean\n ) as string[];\n const uniqueBranches = [...new Set(branches)];\n\n for (const branch of uniqueBranches) {\n const treeData = (await fetchGitHubJson(\n `https://api.github.com/repos/${ownerRepo}/git/trees/${branch}?recursive=1`,\n token\n )) as GitHubTreeResponse | null;\n\n if (treeData) {\n return treeData;\n }\n }\n\n return null;\n};\n\nconst findFolderHashInTree = (\n treeData: GitHubTreeResponse | null,\n skillPath: string\n): string | null => {\n const folderPath = normalizeSkillFolderPath(skillPath);\n\n if (!folderPath) {\n return treeData?.sha ?? null;\n }\n\n if (!Array.isArray(treeData?.tree)) {\n return null;\n }\n\n const folderEntry = treeData.tree.find(\n (entry: GitHubTreeEntry) =>\n entry.type === \"tree\" && entry.path === folderPath\n );\n return folderEntry?.sha ?? null;\n};\n\n// --- Notification helpers ---\n\nconst groupUpdatesByRepo = (updates: UpdateResult[]): GroupedUpdate[] => {\n const grouped = new Map<string, string[]>();\n\n for (const update of updates) {\n const existing = grouped.get(update.repoId) ?? [];\n existing.push(update.skillName);\n grouped.set(update.repoId, existing);\n }\n\n return [...grouped.entries()]\n .map(([repoId, skillNames]) => ({\n repoId,\n skillNames: skillNames.toSorted(),\n }))\n .toSorted((a, b) => a.repoId.localeCompare(b.repoId));\n};\n\nconst buildNotificationBody = (grouped: GroupedUpdate[]): string => {\n const preview = grouped\n .slice(0, 3)\n .map(({ repoId, skillNames }) => `${repoId} (${skillNames.length})`);\n\n if (grouped.length > 3) {\n preview.push(`+${grouped.length - 3} more`);\n }\n\n return preview.join(\", \");\n};\n\nconst buildSignature = (grouped: GroupedUpdate[]): string =>\n createHash(\"sha256\").update(JSON.stringify(grouped)).digest(\"hex\");\n\nconst escapeAppleScript = (value: string): string =>\n value.replaceAll(\"\\\\\", \"\\\\\\\\\").replaceAll('\"', '\\\\\"');\n\nconst sendNotification = (title: string, body: string): void => {\n execFileSync(\n \"/usr/bin/osascript\",\n [\n \"-e\",\n `display notification \"${escapeAppleScript(body)}\" with title \"${escapeAppleScript(title)}\"`,\n ],\n {\n stdio: \"ignore\",\n }\n );\n};\n\nconst writeState = async (\n signature: string | null,\n grouped?: GroupedUpdate[]\n): Promise<void> => {\n await writeJson(STATE_PATH, {\n lastCheckedAt: new Date().toISOString(),\n lastNotifiedSignature: signature,\n ...(grouped ? { updates: grouped } : {}),\n });\n};\n\n// --- Core checker logic ---\n\n// Exported for tests\nexport {\n buildNotificationBody,\n buildSignature,\n findFolderHashInTree,\n getRepoId,\n groupUpdatesByRepo,\n};\n\nconst getTrackedSkills = async (): Promise<TrackedSkill[]> => {\n const lockFile = await readJson<LockFile>(LOCK_PATH, { skills: {} });\n const tracked: TrackedSkill[] = [];\n\n for (const [skillName, entry] of Object.entries(lockFile.skills ?? {})) {\n if (entry?.sourceType !== \"github\") {\n continue;\n }\n\n if (!entry.skillPath || !entry.skillFolderHash) {\n continue;\n }\n\n const repoId = getRepoId(entry);\n if (!repoId) {\n continue;\n }\n\n tracked.push({\n localHash: entry.skillFolderHash,\n repoId,\n skillName,\n skillPath: entry.skillPath,\n });\n }\n\n return tracked;\n};\n\nconst findUpdates = async (): Promise<CheckResult> => {\n const trackedSkills = await getTrackedSkills();\n const token = getGitHubToken();\n const updates: UpdateResult[] = [];\n const errors: CheckError[] = [];\n const treeCache = new Map<\n string,\n Awaited<ReturnType<typeof fetchRepoTree>>\n >();\n\n for (const tracked of trackedSkills) {\n let treeData = treeCache.get(tracked.repoId);\n\n if (treeData === undefined) {\n treeData = await fetchRepoTree(tracked.repoId, token);\n treeCache.set(tracked.repoId, treeData);\n }\n\n const remoteHash = treeData\n ? findFolderHashInTree(treeData, tracked.skillPath)\n : null;\n\n if (!remoteHash) {\n errors.push({\n reason: \"Could not fetch remote folder hash\",\n repoId: tracked.repoId,\n skillName: tracked.skillName,\n });\n continue;\n }\n\n if (remoteHash !== tracked.localHash) {\n updates.push({\n localHash: tracked.localHash,\n remoteHash,\n repoId: tracked.repoId,\n skillName: tracked.skillName,\n });\n }\n }\n\n return { errors, trackedSkills, updates };\n};\n\nconst main = async (): Promise<void> => {\n log(`Using lock file: ${LOCK_PATH}`);\n log(`Using state file: ${STATE_PATH}`);\n\n if (!existsSync(LOCK_PATH)) {\n log(\"No skill lock file found. Nothing to check.\");\n process.exitCode = 0;\n return;\n }\n\n const { trackedSkills, updates, errors } = await findUpdates();\n const grouped: GroupedUpdate[] = groupUpdatesByRepo(updates);\n\n log(`Tracked ${trackedSkills.length} GitHub-backed skill(s).`);\n\n if (errors.length > 0) {\n for (const error of errors) {\n log(`Warning: ${error.repoId}/${error.skillName} - ${error.reason}`);\n }\n }\n\n if (grouped.length === 0) {\n log(\"No updates available.\");\n await writeState(null);\n process.exitCode = 0;\n return;\n }\n\n for (const repo of grouped) {\n log(`Update available: ${repo.repoId} -> ${repo.skillNames.join(\", \")}`);\n }\n\n const state = await readJson<{ lastNotifiedSignature?: string }>(\n STATE_PATH,\n {}\n );\n const signature = buildSignature(grouped);\n\n if (state.lastNotifiedSignature === signature) {\n log(\"Updates already notified. Skipping duplicate notification.\");\n await writeState(signature, grouped);\n process.exitCode = 0;\n return;\n }\n\n const title = \"Skill updates available\";\n const body = buildNotificationBody(grouped);\n\n sendNotification(title, body);\n log(`Notification sent: ${body}`);\n log(\"To update installed skills, run: npx skills update\");\n\n await writeState(signature, grouped);\n};\n\nconst isExecutedDirectly = (): boolean => {\n const [, entryArg] = process.argv;\n\n return entryArg !== undefined && resolve(entryArg) === ENTRYPOINT_PATH;\n};\n\nif (isExecutedDirectly()) {\n try {\n await main();\n } catch (error: unknown) {\n log(\n `Fatal error: ${error instanceof Error ? error.stack || error.message : String(error)}`\n );\n process.exitCode = 1;\n }\n}\n"],"mappings":";;;;;;;;AAsEA,MAAM,OAAO,YAA0B;CACrC,MAAM,6BAAY,IAAI,MAAM,EAAC,aAAa;AAC1C,SAAQ,IAAI,IAAI,UAAU,IAAI,UAAU;;AAG1C,MAAM,WAAW,OAAU,MAAc,aAA4B;AACnE,KAAI;EACF,MAAM,UAAU,MAAM,SAAS,MAAM,OAAO;AAC5C,SAAO,KAAK,MAAM,QAAQ;SACpB;AACN,SAAO;;;AAIX,MAAM,YAAY,OAAO,MAAc,UAAkC;AACvE,OAAM,MAAM,QAAQ,KAAK,EAAE,EAAE,WAAW,MAAM,CAAC;AAC/C,OAAM,UAAU,MAAM,GAAG,KAAK,UAAU,OAAO,MAAM,EAAE,CAAC,KAAK,OAAO;;AAGtE,MAAM,4BAA4B,cAChC,UACG,WAAW,MAAM,IAAI,CACrB,QAAQ,iBAAiB,GAAG,CAC5B,QAAQ,OAAO,GAAG;AAIvB,MAAM,oBAAoB,KACxB,SAAS,EACT,WACA,uBACA,aACD;AACD,MAAM,kBAAkB,cAAc,OAAO,KAAK,IAAI;AACtD,MAAM,qBAAqB,KAAK,mBAAmB,aAAa;AAChE,MAAM,oBAAoB,QAAQ,IAAI,iBAClC,KAAK,QAAQ,IAAI,gBAAgB,UAAU,mBAAmB,GAC9D,KAAK,SAAS,EAAE,WAAW,mBAAmB;AAElD,MAAM,aAAa,QAAQ,IAAI,2BAA2B;AAC1D,MAAM,YAAY,QAAQ,IAAI,0BAA0B;AAIxD,MAAM,aAAa,UAAwC;AACzD,KAAI,OAAO,OAAO,WAAW,YAAY,MAAM,OAAO,SAAS,IAAI,CACjE,QAAO,MAAM,OAAO,QAAQ,UAAU,GAAG;AAG3C,KAAI,OAAO,OAAO,cAAc,UAAU;EACxC,MAAM,WAAW,MAAM,UAAU,MAAM,8BAA8B;AACrE,MAAI,WAAW,GACb,QAAO,SAAS;AAGlB,MAAI;GACF,MAAM,WAAW,IAAI,IAAI,MAAM,UAAU,CAAC,SACvC,QAAQ,QAAQ,GAAG,CACnB,QAAQ,UAAU,GAAG;AACxB,OAAI,SAAS,SAAS,IAAI,CACxB,QAAO;UAEH;;AAKV,QAAO;;AAGT,MAAM,uBAAsC;CAC1C,MAAM,WAAW,QAAQ,IAAI,gBAAgB,QAAQ,IAAI;AACzD,KAAI,SACF,QAAO;CAGT,MAAM,eAAe;EACnB,QAAQ,IAAI;EACZ;EACA;EACA;EACD,CAAC,OAAO,QAAQ;AAEjB,MAAK,MAAM,aAAa,aACtB,KAAI;EACF,MAAM,QAAQ,aAAa,WAAW,CAAC,QAAQ,QAAQ,EAAE;GACvD,UAAU;GACV,OAAO;IAAC;IAAU;IAAQ;IAAS;GACpC,CAAC,CAAC,MAAM;AAET,MAAI,MACF,QAAO;SAEH;AAKV,QAAO;;AAGT,MAAM,kBAAkB,OACtB,KACA,UACqB;AACrB,KAAI;EACF,MAAM,WAAW,MAAM,MAAM,KAAK,EAChC,SAAS;GACP,QAAQ;GACR,cAAc;GACd,GAAI,QAAQ,EAAE,eAAe,UAAU,SAAS,GAAG,EAAE;GACtD,EACF,CAAC;AAEF,MAAI,CAAC,SAAS,GACZ,QAAO;AAGT,SAAO,MAAM,SAAS,MAAM;SACtB;AACN,SAAO;;;AAIX,MAAM,gBAAgB,OACpB,WACA,UACuC;CAMvC,MAAM,WAAW;GALC,MAAM,gBACtB,gCAAgC,aAChC,MACD,GAE2B;EAAgB;EAAQ;EAAS,CAAC,OAC5D,QACD;CACD,MAAM,iBAAiB,CAAC,GAAG,IAAI,IAAI,SAAS,CAAC;AAE7C,MAAK,MAAM,UAAU,gBAAgB;EACnC,MAAM,WAAY,MAAM,gBACtB,gCAAgC,UAAU,aAAa,OAAO,eAC9D,MACD;AAED,MAAI,SACF,QAAO;;AAIX,QAAO;;AAGT,MAAM,wBACJ,UACA,cACkB;CAClB,MAAM,aAAa,yBAAyB,UAAU;AAEtD,KAAI,CAAC,WACH,QAAO,UAAU,OAAO;AAG1B,KAAI,CAAC,MAAM,QAAQ,UAAU,KAAK,CAChC,QAAO;AAOT,QAJoB,SAAS,KAAK,MAC/B,UACC,MAAM,SAAS,UAAU,MAAM,SAAS,WAC3C,EACmB,OAAO;;AAK7B,MAAM,sBAAsB,YAA6C;CACvE,MAAM,0BAAU,IAAI,KAAuB;AAE3C,MAAK,MAAM,UAAU,SAAS;EAC5B,MAAM,WAAW,QAAQ,IAAI,OAAO,OAAO,IAAI,EAAE;AACjD,WAAS,KAAK,OAAO,UAAU;AAC/B,UAAQ,IAAI,OAAO,QAAQ,SAAS;;AAGtC,QAAO,CAAC,GAAG,QAAQ,SAAS,CAAC,CAC1B,KAAK,CAAC,QAAQ,iBAAiB;EAC9B;EACA,YAAY,WAAW,UAAU;EAClC,EAAE,CACF,UAAU,GAAG,MAAM,EAAE,OAAO,cAAc,EAAE,OAAO,CAAC;;AAGzD,MAAM,yBAAyB,YAAqC;CAClE,MAAM,UAAU,QACb,MAAM,GAAG,EAAE,CACX,KAAK,EAAE,QAAQ,iBAAiB,GAAG,OAAO,IAAI,WAAW,OAAO,GAAG;AAEtE,KAAI,QAAQ,SAAS,EACnB,SAAQ,KAAK,IAAI,QAAQ,SAAS,EAAE,OAAO;AAG7C,QAAO,QAAQ,KAAK,KAAK;;AAG3B,MAAM,kBAAkB,YACtB,WAAW,SAAS,CAAC,OAAO,KAAK,UAAU,QAAQ,CAAC,CAAC,OAAO,MAAM;AAEpE,MAAM,qBAAqB,UACzB,MAAM,WAAW,MAAM,OAAO,CAAC,WAAW,MAAK,OAAM;AAEvD,MAAM,oBAAoB,OAAe,SAAuB;AAC9D,cACE,sBACA,CACE,MACA,yBAAyB,kBAAkB,KAAK,CAAC,gBAAgB,kBAAkB,MAAM,CAAC,GAC3F,EACD,EACE,OAAO,UACR,CACF;;AAGH,MAAM,aAAa,OACjB,WACA,YACkB;AAClB,OAAM,UAAU,YAAY;EAC1B,gCAAe,IAAI,MAAM,EAAC,aAAa;EACvC,uBAAuB;EACvB,GAAI,UAAU,EAAE,SAAS,SAAS,GAAG,EAAE;EACxC,CAAC;;AAcJ,MAAM,mBAAmB,YAAqC;CAC5D,MAAM,WAAW,MAAM,SAAmB,WAAW,EAAE,QAAQ,EAAE,EAAE,CAAC;CACpE,MAAM,UAA0B,EAAE;AAElC,MAAK,MAAM,CAAC,WAAW,UAAU,OAAO,QAAQ,SAAS,UAAU,EAAE,CAAC,EAAE;AACtE,MAAI,OAAO,eAAe,SACxB;AAGF,MAAI,CAAC,MAAM,aAAa,CAAC,MAAM,gBAC7B;EAGF,MAAM,SAAS,UAAU,MAAM;AAC/B,MAAI,CAAC,OACH;AAGF,UAAQ,KAAK;GACX,WAAW,MAAM;GACjB;GACA;GACA,WAAW,MAAM;GAClB,CAAC;;AAGJ,QAAO;;AAGT,MAAM,cAAc,YAAkC;CACpD,MAAM,gBAAgB,MAAM,kBAAkB;CAC9C,MAAM,QAAQ,gBAAgB;CAC9B,MAAM,UAA0B,EAAE;CAClC,MAAM,SAAuB,EAAE;CAC/B,MAAM,4BAAY,IAAI,KAGnB;AAEH,MAAK,MAAM,WAAW,eAAe;EACnC,IAAI,WAAW,UAAU,IAAI,QAAQ,OAAO;AAE5C,MAAI,aAAa,KAAA,GAAW;AAC1B,cAAW,MAAM,cAAc,QAAQ,QAAQ,MAAM;AACrD,aAAU,IAAI,QAAQ,QAAQ,SAAS;;EAGzC,MAAM,aAAa,WACf,qBAAqB,UAAU,QAAQ,UAAU,GACjD;AAEJ,MAAI,CAAC,YAAY;AACf,UAAO,KAAK;IACV,QAAQ;IACR,QAAQ,QAAQ;IAChB,WAAW,QAAQ;IACpB,CAAC;AACF;;AAGF,MAAI,eAAe,QAAQ,UACzB,SAAQ,KAAK;GACX,WAAW,QAAQ;GACnB;GACA,QAAQ,QAAQ;GAChB,WAAW,QAAQ;GACpB,CAAC;;AAIN,QAAO;EAAE;EAAQ;EAAe;EAAS;;AAG3C,MAAM,OAAO,YAA2B;AACtC,KAAI,oBAAoB,YAAY;AACpC,KAAI,qBAAqB,aAAa;AAEtC,KAAI,CAAC,WAAW,UAAU,EAAE;AAC1B,MAAI,8CAA8C;AAClD,UAAQ,WAAW;AACnB;;CAGF,MAAM,EAAE,eAAe,SAAS,WAAW,MAAM,aAAa;CAC9D,MAAM,UAA2B,mBAAmB,QAAQ;AAE5D,KAAI,WAAW,cAAc,OAAO,0BAA0B;AAE9D,KAAI,OAAO,SAAS,EAClB,MAAK,MAAM,SAAS,OAClB,KAAI,YAAY,MAAM,OAAO,GAAG,MAAM,UAAU,KAAK,MAAM,SAAS;AAIxE,KAAI,QAAQ,WAAW,GAAG;AACxB,MAAI,wBAAwB;AAC5B,QAAM,WAAW,KAAK;AACtB,UAAQ,WAAW;AACnB;;AAGF,MAAK,MAAM,QAAQ,QACjB,KAAI,qBAAqB,KAAK,OAAO,MAAM,KAAK,WAAW,KAAK,KAAK,GAAG;CAG1E,MAAM,QAAQ,MAAM,SAClB,YACA,EAAE,CACH;CACD,MAAM,YAAY,eAAe,QAAQ;AAEzC,KAAI,MAAM,0BAA0B,WAAW;AAC7C,MAAI,6DAA6D;AACjE,QAAM,WAAW,WAAW,QAAQ;AACpC,UAAQ,WAAW;AACnB;;CAGF,MAAM,QAAQ;CACd,MAAM,OAAO,sBAAsB,QAAQ;AAE3C,kBAAiB,OAAO,KAAK;AAC7B,KAAI,sBAAsB,OAAO;AACjC,KAAI,qDAAqD;AAEzD,OAAM,WAAW,WAAW,QAAQ;;AAGtC,MAAM,2BAAoC;CACxC,MAAM,GAAG,YAAY,QAAQ;AAE7B,QAAO,aAAa,KAAA,KAAa,QAAQ,SAAS,KAAK;;AAGzD,IAAI,oBAAoB,CACtB,KAAI;AACF,OAAM,MAAM;SACL,OAAgB;AACvB,KACE,gBAAgB,iBAAiB,QAAQ,MAAM,SAAS,MAAM,UAAU,OAAO,MAAM,GACtF;AACD,SAAQ,WAAW"}
|
package/dist/cli.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","names":[],"sources":["../src/cli.ts"],"sourcesContent":["import { execFileSync } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport { copyFile, mkdir, readFile, rm, writeFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nconst { version, name } = await import(\"../package.json\", {\n with: { type: \"json\" },\n}).then((m) => m.default);\n\nconst ENTRYPOINT_PATH = fileURLToPath(import.meta.url);\nconst PACKAGE_DIR = dirname(ENTRYPOINT_PATH);\nconst CHECKER_SOURCE_PATH = join(PACKAGE_DIR, \"checker.js\");\nconst COMMAND_NAME = name.split(\"/\").at(-1) ?? \"skillwatch\";\nconst APP_DIR_NAME = \"skillwatch\";\nconst LEGACY_APP_DIR_NAME = \"skills-update-notifier\";\n\n// --- Utilities ---\n\nconst fail = (message: string): never => {\n console.error(message);\n process.exit(1);\n};\n\nexport const parseInteger = (\n value: string,\n flagName: string,\n min: number,\n max: number\n): number => {\n const parsed = Number.parseInt(value, 10);\n\n if (!Number.isInteger(parsed) || parsed < min || parsed > max) {\n fail(`${flagName} must be an integer between ${min} and ${max}.`);\n }\n\n return parsed;\n};\n\n// --- LaunchAgent management ---\n\ninterface InstallOptions {\n hour: number;\n minute: number;\n}\n\nconst LABEL = \"com.mblode.skillwatch\";\nconst LEGACY_LABEL = \"com.mblode.skills-check\";\n\nconst getSupportDir = (dirName: string): string =>\n join(homedir(), \"Library\", \"Application Support\", dirName);\n\nconst getAppDir = (): string => getSupportDir(APP_DIR_NAME);\n\nconst getLegacyAppDir = (): string => getSupportDir(LEGACY_APP_DIR_NAME);\n\nconst getLogDirForName = (dirName: string): string =>\n join(homedir(), \"Library\", \"Logs\", dirName);\n\nconst getLogDir = (): string => getLogDirForName(APP_DIR_NAME);\n\nconst getLegacyLogDir = (): string => getLogDirForName(LEGACY_APP_DIR_NAME);\n\nconst getPlistTargetPath = (label = LABEL): string =>\n join(homedir(), \"Library\", \"LaunchAgents\", `${label}.plist`);\n\nconst assertMacOS = (): void => {\n if (process.platform !== \"darwin\") {\n fail(\n `${COMMAND_NAME} only supports macOS because it uses launchd and osascript.`\n );\n }\n};\n\nconst requireCommand = (command: string): void => {\n try {\n execFileSync(\"which\", [command], {\n stdio: \"ignore\",\n });\n } catch {\n fail(`Missing required command: ${command}`);\n }\n};\n\nconst getUid = (): number => {\n if (typeof process.getuid !== \"function\") {\n fail(\"Could not determine the current user id.\");\n }\n\n // Safe: guarded above, fail() returns never\n return (process.getuid as () => number)();\n};\n\nconst writePlist = async (\n templatePath: string,\n options: InstallOptions,\n paths: {\n checkerTargetPath: string;\n stdoutPath: string;\n stderrPath: string;\n }\n): Promise<void> => {\n const plistTargetPath = getPlistTargetPath();\n const template = await readFile(templatePath, \"utf8\");\n const content = template\n .replaceAll(\"__NODE_BIN__\", process.execPath)\n .replaceAll(\"__SCRIPT_PATH__\", paths.checkerTargetPath)\n .replaceAll(\"__HOUR__\", String(options.hour))\n .replaceAll(\"__MINUTE__\", String(options.minute))\n .replaceAll(\"__STDOUT_PATH__\", paths.stdoutPath)\n .replaceAll(\"__STDERR_PATH__\", paths.stderrPath);\n\n await mkdir(dirname(plistTargetPath), { recursive: true });\n await writeFile(plistTargetPath, content, \"utf8\");\n};\n\nconst lintPlist = (): void => {\n execFileSync(\"plutil\", [\"-lint\", getPlistTargetPath()], {\n stdio: \"ignore\",\n });\n};\n\nconst bootoutIfLoaded = (label = LABEL): void => {\n try {\n execFileSync(\"launchctl\", [\"bootout\", `gui/${getUid()}/${label}`], {\n stdio: \"ignore\",\n });\n } catch {\n // Ignore when the agent is not loaded.\n }\n};\n\nconst bootstrapAgent = (): void => {\n execFileSync(\n \"launchctl\",\n [\"bootstrap\", `gui/${getUid()}`, getPlistTargetPath()],\n {\n stdio: \"inherit\",\n }\n );\n};\n\n// --- CLI commands ---\n\nconst printUsage = (): void => {\n console.log(\n `\n${name} ${version}\n\nUsage:\n ${COMMAND_NAME} install [--hour 9] [--minute 0]\n ${COMMAND_NAME} uninstall\n ${COMMAND_NAME} check-now\n ${COMMAND_NAME} --help\n ${COMMAND_NAME} --version\n\nCommands:\n install Install the daily LaunchAgent and checker script\n uninstall Remove the LaunchAgent, checker script, and logs\n check-now Run the checker immediately\n\nOptions for install:\n --hour <0-23> Daily check hour, default 9\n --minute <0-59> Daily check minute, default 0\n`.trim()\n );\n};\n\nconst parseInstallOptions = (args: string[]): InstallOptions => {\n const options: InstallOptions = {\n hour: parseInteger(process.env.CHECK_HOUR ?? \"9\", \"CHECK_HOUR\", 0, 23),\n minute: parseInteger(\n process.env.CHECK_MINUTE ?? \"0\",\n \"CHECK_MINUTE\",\n 0,\n 59\n ),\n };\n\n for (let index = 0; index < args.length; index += 1) {\n const current = args[index];\n\n if (current === \"--help\" || current === \"-h\") {\n printUsage();\n process.exit(0);\n }\n\n if (current === \"--hour\") {\n index += 1;\n options.hour = parseInteger(args[index] ?? \"\", \"--hour\", 0, 23);\n continue;\n }\n\n if (current === \"--minute\") {\n index += 1;\n options.minute = parseInteger(args[index] ?? \"\", \"--minute\", 0, 59);\n continue;\n }\n\n fail(`Unknown install option: ${current}`);\n }\n\n return options;\n};\n\nconst installCommand = async (args: string[]): Promise<void> => {\n assertMacOS();\n requireCommand(\"launchctl\");\n requireCommand(\"plutil\");\n\n const options = parseInstallOptions(args);\n const appDir = getAppDir();\n const logDir = getLogDir();\n const checkerTargetPath = join(appDir, \"checker.js\");\n const plistTemplatePath = join(\n PACKAGE_DIR,\n \"com.mblode.skillwatch.plist.template\"\n );\n\n await mkdir(appDir, { recursive: true });\n await mkdir(logDir, { recursive: true });\n await copyFile(CHECKER_SOURCE_PATH, checkerTargetPath);\n await writePlist(plistTemplatePath, options, {\n checkerTargetPath,\n stderrPath: join(logDir, \"stderr.log\"),\n stdoutPath: join(logDir, \"stdout.log\"),\n });\n lintPlist();\n\n bootoutIfLoaded();\n bootstrapAgent();\n\n console.log(\n `\nInstalled ${COMMAND_NAME}.\n\nFiles:\n agent: ${getPlistTargetPath()}\n script: ${checkerTargetPath}\n logs: ${logDir}\n\nCheck now:\n npx ${COMMAND_NAME} check-now\n launchctl kickstart -k gui/${getUid()}/${LABEL}\n`.trim()\n );\n};\n\nconst uninstallCommand = async (): Promise<void> => {\n assertMacOS();\n requireCommand(\"launchctl\");\n\n bootoutIfLoaded();\n bootoutIfLoaded(LEGACY_LABEL);\n await rm(getPlistTargetPath(), { force: true });\n await rm(getPlistTargetPath(LEGACY_LABEL), { force: true });\n await rm(getAppDir(), { force: true, recursive: true });\n await rm(getLegacyAppDir(), { force: true, recursive: true });\n await rm(getLogDir(), { force: true, recursive: true });\n await rm(getLegacyLogDir(), { force: true, recursive: true });\n\n console.log(`Uninstalled ${COMMAND_NAME}.`);\n};\n\nconst checkNowCommand = (): void => {\n assertMacOS();\n const appDir = getAppDir();\n const installedChecker = join(appDir, \"checker.js\");\n const checkerPath = existsSync(installedChecker)\n ? installedChecker\n : CHECKER_SOURCE_PATH;\n\n execFileSync(process.execPath, [checkerPath], {\n env: process.env,\n stdio: \"inherit\",\n });\n};\n\nconst main = async (): Promise<void> => {\n const [rawCommand, ...rest] = process.argv.slice(2);\n const command = rawCommand === \"--uninstall\" ? \"uninstall\" : rawCommand;\n\n if (\n !command ||\n command === \"--help\" ||\n command === \"-h\" ||\n command === \"help\"\n ) {\n printUsage();\n return;\n }\n\n if (command === \"--version\" || command === \"-v\" || command === \"version\") {\n console.log(version);\n return;\n }\n\n if (command === \"install\") {\n await installCommand(rest);\n return;\n }\n\n if (command === \"uninstall\") {\n await uninstallCommand();\n return;\n }\n\n if (command === \"check-now\") {\n checkNowCommand();\n return;\n }\n\n fail(`Unknown command: ${command}`);\n};\n\nconst isExecutedDirectly = (): boolean => {\n const [, entryArg] = process.argv;\n\n return entryArg !== undefined && resolve(entryArg) === ENTRYPOINT_PATH;\n};\n\nif (isExecutedDirectly()) {\n try {\n await main();\n } catch (error: unknown) {\n fail(error instanceof Error ? error.stack || error.message : String(error));\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
-
<plist version="1.0">
|
|
4
|
-
<dict>
|
|
5
|
-
<key>Label</key>
|
|
6
|
-
<string>com.mblode.skillwatch</string>
|
|
7
|
-
|
|
8
|
-
<key>ProgramArguments</key>
|
|
9
|
-
<array>
|
|
10
|
-
<string>__NODE_BIN__</string>
|
|
11
|
-
<string>__SCRIPT_PATH__</string>
|
|
12
|
-
</array>
|
|
13
|
-
|
|
14
|
-
<key>StartCalendarInterval</key>
|
|
15
|
-
<dict>
|
|
16
|
-
<key>Hour</key>
|
|
17
|
-
<integer>__HOUR__</integer>
|
|
18
|
-
<key>Minute</key>
|
|
19
|
-
<integer>__MINUTE__</integer>
|
|
20
|
-
</dict>
|
|
21
|
-
|
|
22
|
-
<key>StandardOutPath</key>
|
|
23
|
-
<string>__STDOUT_PATH__</string>
|
|
24
|
-
|
|
25
|
-
<key>StandardErrorPath</key>
|
|
26
|
-
<string>__STDERR_PATH__</string>
|
|
27
|
-
</dict>
|
|
28
|
-
</plist>
|
package/dist/package-D8pw1JVM.js
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
//#region package.json
|
|
2
|
-
var package_default = {
|
|
3
|
-
name: "skillwatch",
|
|
4
|
-
version: "0.1.0",
|
|
5
|
-
description: "Daily macOS notifications when installed GitHub-backed skills have updates.",
|
|
6
|
-
keywords: [
|
|
7
|
-
"agent-skills",
|
|
8
|
-
"launchd",
|
|
9
|
-
"macos",
|
|
10
|
-
"notification",
|
|
11
|
-
"skills"
|
|
12
|
-
],
|
|
13
|
-
homepage: "https://github.com/mblode/update-skills#readme",
|
|
14
|
-
bugs: { "url": "https://github.com/mblode/update-skills/issues" },
|
|
15
|
-
license: "MIT",
|
|
16
|
-
author: "Matt Blode",
|
|
17
|
-
repository: {
|
|
18
|
-
"type": "git",
|
|
19
|
-
"url": "git+https://github.com/mblode/update-skills.git"
|
|
20
|
-
},
|
|
21
|
-
bin: "./dist/cli.js",
|
|
22
|
-
files: [
|
|
23
|
-
"dist",
|
|
24
|
-
"README.md",
|
|
25
|
-
"LICENSE.md"
|
|
26
|
-
],
|
|
27
|
-
type: "module",
|
|
28
|
-
publishConfig: { "access": "public" },
|
|
29
|
-
scripts: {
|
|
30
|
-
"build": "tsdown && cp com.mblode.skillwatch.plist.template dist/",
|
|
31
|
-
"dev": "tsdown --watch",
|
|
32
|
-
"typecheck": "tsc --noEmit",
|
|
33
|
-
"check": "ultracite check",
|
|
34
|
-
"fix": "ultracite fix",
|
|
35
|
-
"test": "vitest run",
|
|
36
|
-
"smoke": "SKILLS_CHECK_LOCK_PATH=/tmp/skillwatch-missing-lock.json SKILLS_CHECK_STATE_PATH=/tmp/skillwatch-state.json node dist/checker.js && node dist/cli.js --help && node dist/cli.js --version",
|
|
37
|
-
"validate": "npm run build && npm run typecheck && npm run check && npm run test && npm run smoke",
|
|
38
|
-
"pack:dry-run": "npm pack --dry-run",
|
|
39
|
-
"changeset": "changeset",
|
|
40
|
-
"release": "npm run build && changeset publish",
|
|
41
|
-
"prepare": "git rev-parse --is-inside-work-tree >/dev/null 2>&1 && lefthook install || true"
|
|
42
|
-
},
|
|
43
|
-
devDependencies: {
|
|
44
|
-
"@changesets/cli": "^2.29.0",
|
|
45
|
-
"@types/node": "^22.15.0",
|
|
46
|
-
"lefthook": "^2.1.4",
|
|
47
|
-
"oxfmt": "^0.41.0",
|
|
48
|
-
"oxlint": "^1.56.0",
|
|
49
|
-
"tsdown": "^0.12.0",
|
|
50
|
-
"typescript": "^5.8.0",
|
|
51
|
-
"ultracite": "^7.3.2",
|
|
52
|
-
"vitest": "^3.1.0"
|
|
53
|
-
},
|
|
54
|
-
engines: { "node": ">=22" }
|
|
55
|
-
};
|
|
56
|
-
//#endregion
|
|
57
|
-
export { package_default as default };
|
|
58
|
-
|
|
59
|
-
//# sourceMappingURL=package-D8pw1JVM.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"package-D8pw1JVM.js","names":[],"sources":["../package.json"],"sourcesContent":[""],"mappings":""}
|