@dadado/agent-kit-cli 5.15.1 → 5.16.0
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/dashboard/dashboard-data.mjs +69 -65
- package/dashboard/lib/guards.d.mts +5 -0
- package/dashboard/lib/guards.mjs +31 -0
- package/dashboard/lib/identity-probe.mjs +56 -0
- package/dashboard/lib/pipeline-cache.mjs +58 -0
- package/dashboard/lib/processes.mjs +71 -0
- package/dashboard/serve.mjs +43 -1
- package/dashboard/start-broadcast.mjs +2 -12
- package/dashboard/start.mjs +2 -13
- package/dist/add-7UMSGVWC.js +150 -0
- package/dist/chunk-2IPXQFJE.js +253 -0
- package/dist/chunk-2YU2H7I6.js +42 -0
- package/dist/chunk-4IWG5CVE.js +214 -0
- package/dist/chunk-5OXPNVCF.js +106 -0
- package/dist/chunk-5VCOWTTP.js +6 -0
- package/dist/chunk-6QV2BFGA.js +341 -0
- package/dist/chunk-77V75A7O.js +39 -0
- package/dist/chunk-7HDQEK6T.js +26 -0
- package/dist/chunk-CBCW5R5J.js +484 -0
- package/dist/chunk-CGTE7DHN.js +410 -0
- package/dist/chunk-DE3LQFSJ.js +18 -0
- package/dist/chunk-F6C3GFKV.js +57 -0
- package/dist/chunk-HFUH3HVS.js +282 -0
- package/dist/chunk-HS7LVVLO.js +74 -0
- package/dist/chunk-IHROBTNM.js +135 -0
- package/dist/chunk-KD2MYBB6.js +213 -0
- package/dist/chunk-KI6ZHBPV.js +332 -0
- package/dist/chunk-MAPOQEA2.js +43 -0
- package/dist/chunk-NQ7IQ5XT.js +26 -0
- package/dist/chunk-OUQZC744.js +1315 -0
- package/dist/chunk-OZ4QV6LM.js +1078 -0
- package/dist/chunk-PCCQUWE5.js +817 -0
- package/dist/chunk-PNFF6OU3.js +273 -0
- package/dist/chunk-QTON2GQX.js +203 -0
- package/dist/chunk-TFZ2JUXH.js +1081 -0
- package/dist/chunk-VGZBARQC.js +318 -0
- package/dist/chunk-VS2JD2XC.js +1453 -0
- package/dist/chunk-VY7MTPJ2.js +205 -0
- package/dist/chunk-WW3YZKHW.js +165 -0
- package/dist/chunk-XWPO3VYQ.js +53 -0
- package/dist/chunk-XZZAVHH4.js +503 -0
- package/dist/chunk-YGBBHKBE.js +82 -0
- package/dist/chunk-ZB43YMHW.js +20 -0
- package/dist/chunk-ZCTSUQRR.js +874 -0
- package/dist/chunk-ZXKZBTXE.js +17 -0
- package/dist/contribute-YKK6YCSI.js +367 -0
- package/dist/cursor-awareness-KTL2RGKD.js +77 -0
- package/dist/dashboard-YSNUGBFY.js +21 -0
- package/dist/dashboard-broadcast-M4CMW7RX.js +119 -0
- package/dist/diff-YTR7VOAN.js +74 -0
- package/dist/doctor-TA2FWREU.js +497 -0
- package/dist/dogfood-KKFVRWT6.js +366 -0
- package/dist/guard-EHXF76ID.js +530 -0
- package/dist/handoff-3UI6VKVG.js +302 -0
- package/dist/hook-MUV6Y7UW.js +1074 -0
- package/dist/index.js +62 -14068
- package/dist/init-TTTKGC4C.js +127 -0
- package/dist/install-ZMFJBPOI.js +34 -0
- package/dist/mission-control-LJACEVVN.js +89 -0
- package/dist/monitors-QV2IS3MP.js +176 -0
- package/dist/plan-index-JMC7Z4DW.js +44 -0
- package/dist/public-inbound-radar-WP3XF7GS.js +68 -0
- package/dist/remote-issues-SZ4HDQL5.js +44 -0
- package/dist/run-I3BFGULT.js +243 -0
- package/dist/run-plan-FO7CL4M3.js +118 -0
- package/dist/scan-NI2SWJ2E.js +33 -0
- package/dist/setup-global-JUVMJXJ5.js +340 -0
- package/dist/status-MVCUNCWX.js +138 -0
- package/dist/update-Q4EBIRUB.js +260 -0
- package/dist/validate-V2CW7J4B.js +201 -0
- package/package.json +1 -1
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import {
|
|
2
|
+
formatSkippedSymlinks,
|
|
3
|
+
logApplyStats
|
|
4
|
+
} from "./chunk-HS7LVVLO.js";
|
|
5
|
+
import {
|
|
6
|
+
REGISTRY_CLI_ARGS,
|
|
7
|
+
resolveRegistryFromCli
|
|
8
|
+
} from "./chunk-XWPO3VYQ.js";
|
|
9
|
+
import {
|
|
10
|
+
logger
|
|
11
|
+
} from "./chunk-ZB43YMHW.js";
|
|
12
|
+
import {
|
|
13
|
+
allPacks,
|
|
14
|
+
allSkills,
|
|
15
|
+
buildManifest,
|
|
16
|
+
findPack,
|
|
17
|
+
findSkill,
|
|
18
|
+
installPack,
|
|
19
|
+
installSkill,
|
|
20
|
+
loadAgentKitManifest,
|
|
21
|
+
loadRegistry,
|
|
22
|
+
resolveProtectedGlobs,
|
|
23
|
+
saveManifest,
|
|
24
|
+
upsertIdList
|
|
25
|
+
} from "./chunk-VS2JD2XC.js";
|
|
26
|
+
import "./chunk-5VCOWTTP.js";
|
|
27
|
+
import {
|
|
28
|
+
KIT_VERSION
|
|
29
|
+
} from "./chunk-7HDQEK6T.js";
|
|
30
|
+
import "./chunk-77V75A7O.js";
|
|
31
|
+
|
|
32
|
+
// src/commands/add.ts
|
|
33
|
+
import { defineCommand } from "citty";
|
|
34
|
+
var addCommand = defineCommand({
|
|
35
|
+
meta: {
|
|
36
|
+
name: "add",
|
|
37
|
+
description: "Install a skill or L1 pack from the registry."
|
|
38
|
+
},
|
|
39
|
+
args: {
|
|
40
|
+
id: {
|
|
41
|
+
type: "positional",
|
|
42
|
+
description: "Skill id or pack id from registry"
|
|
43
|
+
},
|
|
44
|
+
skill: {
|
|
45
|
+
type: "boolean",
|
|
46
|
+
description: "Force treat id as a skill (when pack shares the same id)",
|
|
47
|
+
default: false
|
|
48
|
+
},
|
|
49
|
+
pack: {
|
|
50
|
+
type: "boolean",
|
|
51
|
+
description: "Force treat id as a pack (when skill shares the same id)",
|
|
52
|
+
default: false
|
|
53
|
+
},
|
|
54
|
+
cwd: {
|
|
55
|
+
type: "string",
|
|
56
|
+
default: process.cwd()
|
|
57
|
+
},
|
|
58
|
+
...REGISTRY_CLI_ARGS
|
|
59
|
+
},
|
|
60
|
+
async run({ args }) {
|
|
61
|
+
if (!args.id) throw new Error("Provide an id: agent-kit add <skill|pack>");
|
|
62
|
+
if (args.skill && args.pack) {
|
|
63
|
+
throw new Error("Use only one of --skill or --pack");
|
|
64
|
+
}
|
|
65
|
+
const existing = await loadAgentKitManifest(args.cwd, { onWarning: logger.warn });
|
|
66
|
+
const registry = await resolveRegistryFromCli({
|
|
67
|
+
cwd: args.cwd,
|
|
68
|
+
registry: args.registry,
|
|
69
|
+
url: args.url,
|
|
70
|
+
ref: args.ref,
|
|
71
|
+
refresh: args.refresh,
|
|
72
|
+
manifest: existing
|
|
73
|
+
});
|
|
74
|
+
try {
|
|
75
|
+
const protectedGlobs = resolveProtectedGlobs(existing);
|
|
76
|
+
const index = await loadRegistry(registry.root);
|
|
77
|
+
const asSkill = allSkills(index).find((s) => s.id === args.id);
|
|
78
|
+
const asPack = allPacks(index).find((p) => p.id === args.id);
|
|
79
|
+
const base = existing ? { ...existing } : buildManifest({
|
|
80
|
+
version: KIT_VERSION,
|
|
81
|
+
profile: "default",
|
|
82
|
+
registryUrl: registry.url,
|
|
83
|
+
registryRef: registry.ref
|
|
84
|
+
});
|
|
85
|
+
const skipped = (stats) => {
|
|
86
|
+
if (stats.skippedSymlink.length === 0) return false;
|
|
87
|
+
logger.error(formatSkippedSymlinks(stats.skippedSymlink));
|
|
88
|
+
process.exitCode = 1;
|
|
89
|
+
return true;
|
|
90
|
+
};
|
|
91
|
+
const finish = async (kind, id) => {
|
|
92
|
+
if (kind === "skill") base.skills = upsertIdList(base.skills, id);
|
|
93
|
+
else base.packs = upsertIdList(base.packs, id);
|
|
94
|
+
if (registry.url || registry.ref) {
|
|
95
|
+
base.registry = {
|
|
96
|
+
url: registry.url ?? base.registry?.url,
|
|
97
|
+
ref: registry.ref ?? base.registry?.ref
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
base.version = KIT_VERSION;
|
|
101
|
+
await saveManifest(args.cwd, base);
|
|
102
|
+
};
|
|
103
|
+
if (args.skill) {
|
|
104
|
+
const skill = await findSkill(registry.root, args.id);
|
|
105
|
+
const stats = await installSkill(registry.root, args.cwd, skill, { protectedGlobs });
|
|
106
|
+
logApplyStats(stats);
|
|
107
|
+
if (skipped(stats)) return;
|
|
108
|
+
await finish("skill", skill.id);
|
|
109
|
+
logger.success(`Skill '${skill.id}' added.`);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
if (args.pack) {
|
|
113
|
+
const pack = await findPack(registry.root, args.id);
|
|
114
|
+
const stats = await installPack(registry.root, args.cwd, pack.id, { protectedGlobs });
|
|
115
|
+
logApplyStats(stats);
|
|
116
|
+
if (skipped(stats)) return;
|
|
117
|
+
await finish("pack", pack.id);
|
|
118
|
+
logger.success(`Pack '${pack.id}' added.`);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
if (asSkill && asPack) {
|
|
122
|
+
throw new Error(
|
|
123
|
+
`'${args.id}' is both a skill and a pack. Disambiguate: agent-kit add --skill ${args.id} | agent-kit add --pack ${args.id}`
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
if (asSkill) {
|
|
127
|
+
const stats = await installSkill(registry.root, args.cwd, asSkill, { protectedGlobs });
|
|
128
|
+
logApplyStats(stats);
|
|
129
|
+
if (skipped(stats)) return;
|
|
130
|
+
await finish("skill", asSkill.id);
|
|
131
|
+
logger.success(`Skill '${asSkill.id}' added.`);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
if (asPack) {
|
|
135
|
+
const stats = await installPack(registry.root, args.cwd, asPack.id, { protectedGlobs });
|
|
136
|
+
logApplyStats(stats);
|
|
137
|
+
if (skipped(stats)) return;
|
|
138
|
+
await finish("pack", asPack.id);
|
|
139
|
+
logger.success(`Pack '${asPack.id}' added.`);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
throw new Error(`'${args.id}' not found as skill or pack in registry.`);
|
|
143
|
+
} finally {
|
|
144
|
+
await registry.unlock?.();
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
export {
|
|
149
|
+
addCommand
|
|
150
|
+
};
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import {
|
|
2
|
+
contextConfigPath,
|
|
3
|
+
intervalElapsed,
|
|
4
|
+
loadContextConfig
|
|
5
|
+
} from "./chunk-NQ7IQ5XT.js";
|
|
6
|
+
import {
|
|
7
|
+
fileExists,
|
|
8
|
+
writeJson
|
|
9
|
+
} from "./chunk-77V75A7O.js";
|
|
10
|
+
|
|
11
|
+
// src/lifecycle/public-inbound-radar.ts
|
|
12
|
+
import { execFile } from "child_process";
|
|
13
|
+
import { access } from "fs/promises";
|
|
14
|
+
import path from "path";
|
|
15
|
+
import { promisify } from "util";
|
|
16
|
+
var execFileAsync = promisify(execFile);
|
|
17
|
+
var PUBLIC_INBOUND_REPO = "agent-kit-startup/agent-kit";
|
|
18
|
+
var DEFAULT_PREFS = {
|
|
19
|
+
enabled: false,
|
|
20
|
+
intervalDays: 7,
|
|
21
|
+
lastCheckedAt: null
|
|
22
|
+
};
|
|
23
|
+
function emptySummary() {
|
|
24
|
+
return { issues: 0, humanPullRequests: 0, dependabotPullRequests: 0, total: 0 };
|
|
25
|
+
}
|
|
26
|
+
function summarize(items) {
|
|
27
|
+
const summary = emptySummary();
|
|
28
|
+
for (const item of items) {
|
|
29
|
+
summary.total += 1;
|
|
30
|
+
if (item.class === "issue") summary.issues += 1;
|
|
31
|
+
else if (item.class === "dependabot") summary.dependabotPullRequests += 1;
|
|
32
|
+
else summary.humanPullRequests += 1;
|
|
33
|
+
}
|
|
34
|
+
return summary;
|
|
35
|
+
}
|
|
36
|
+
function labelNames(labels) {
|
|
37
|
+
if (!Array.isArray(labels)) return [];
|
|
38
|
+
return labels.map((entry) => typeof entry === "string" ? entry : entry?.name).filter((name) => typeof name === "string" && name.length > 0);
|
|
39
|
+
}
|
|
40
|
+
function isDependabotAuthor(login) {
|
|
41
|
+
if (!login) return false;
|
|
42
|
+
const normalized = login.toLowerCase();
|
|
43
|
+
return normalized === "dependabot" || normalized === "dependabot[bot]" || normalized === "app/dependabot" || normalized.startsWith("dependabot") || normalized === "renovate" || normalized === "renovate[bot]";
|
|
44
|
+
}
|
|
45
|
+
function classifyInbound(kind, authorLogin) {
|
|
46
|
+
if (kind === "issue") return "issue";
|
|
47
|
+
return isDependabotAuthor(authorLogin) ? "dependabot" : "human-pr";
|
|
48
|
+
}
|
|
49
|
+
function readPublicInboundCheckPrefs(config) {
|
|
50
|
+
if (!config || typeof config !== "object" || Array.isArray(config)) {
|
|
51
|
+
return { ...DEFAULT_PREFS };
|
|
52
|
+
}
|
|
53
|
+
const raw = config.publicInboundCheck;
|
|
54
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
55
|
+
return { ...DEFAULT_PREFS };
|
|
56
|
+
}
|
|
57
|
+
const uc = raw;
|
|
58
|
+
const intervalDays = typeof uc.intervalDays === "number" && Number.isInteger(uc.intervalDays) && uc.intervalDays >= 1 ? uc.intervalDays : DEFAULT_PREFS.intervalDays;
|
|
59
|
+
return {
|
|
60
|
+
enabled: uc.enabled === true,
|
|
61
|
+
intervalDays,
|
|
62
|
+
lastCheckedAt: typeof uc.lastCheckedAt === "string" ? uc.lastCheckedAt : null
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
async function stampPublicInboundCheck(cwd, checkedAt) {
|
|
66
|
+
const existing = await loadContextConfig(cwd) ?? {};
|
|
67
|
+
const prev = existing.publicInboundCheck && typeof existing.publicInboundCheck === "object" ? { ...existing.publicInboundCheck } : {};
|
|
68
|
+
existing.publicInboundCheck = {
|
|
69
|
+
...DEFAULT_PREFS,
|
|
70
|
+
...prev,
|
|
71
|
+
lastCheckedAt: (checkedAt ?? /* @__PURE__ */ new Date()).toISOString()
|
|
72
|
+
};
|
|
73
|
+
await writeJson(contextConfigPath(cwd), existing);
|
|
74
|
+
}
|
|
75
|
+
function mapRows(kind, rows) {
|
|
76
|
+
const items = [];
|
|
77
|
+
for (const row of rows) {
|
|
78
|
+
const number = typeof row.number === "number" ? row.number : Number(row.number);
|
|
79
|
+
if (!Number.isFinite(number) || number <= 0) continue;
|
|
80
|
+
const author = row.author?.login?.trim() || "unknown";
|
|
81
|
+
const title = typeof row.title === "string" && row.title.trim() ? row.title.trim() : "(no title)";
|
|
82
|
+
const updatedAt = typeof row.updatedAt === "string" && row.updatedAt.trim() ? row.updatedAt.trim() : "";
|
|
83
|
+
const url = typeof row.url === "string" && row.url.trim() ? row.url.trim() : `https://github.com/${PUBLIC_INBOUND_REPO}/${kind === "issue" ? "issues" : "pull"}/${number}`;
|
|
84
|
+
items.push({
|
|
85
|
+
kind,
|
|
86
|
+
class: classifyInbound(kind, author),
|
|
87
|
+
number,
|
|
88
|
+
title,
|
|
89
|
+
author,
|
|
90
|
+
updatedAt,
|
|
91
|
+
url,
|
|
92
|
+
labels: labelNames(row.labels),
|
|
93
|
+
cite: `${PUBLIC_INBOUND_REPO}#${number}`
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
return items;
|
|
97
|
+
}
|
|
98
|
+
async function defaultRunGh(args, options) {
|
|
99
|
+
const { stdout } = await execFileAsync("gh", args, {
|
|
100
|
+
cwd: options?.cwd,
|
|
101
|
+
encoding: "utf8",
|
|
102
|
+
maxBuffer: 4 * 1024 * 1024,
|
|
103
|
+
timeout: 3e4
|
|
104
|
+
});
|
|
105
|
+
return stdout;
|
|
106
|
+
}
|
|
107
|
+
async function ghAvailable(runGh, cwd) {
|
|
108
|
+
try {
|
|
109
|
+
await runGh(["--version"], { cwd });
|
|
110
|
+
return true;
|
|
111
|
+
} catch {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async function detectFactoryCheckout(cwd) {
|
|
116
|
+
const root = path.resolve(cwd);
|
|
117
|
+
try {
|
|
118
|
+
const { stdout } = await execFileAsync("git", ["remote", "get-url", "origin"], {
|
|
119
|
+
cwd: root,
|
|
120
|
+
encoding: "utf8",
|
|
121
|
+
timeout: 5e3
|
|
122
|
+
});
|
|
123
|
+
if (/agent-kit-dev/i.test(stdout)) return true;
|
|
124
|
+
} catch {
|
|
125
|
+
}
|
|
126
|
+
return fileExists(path.join(root, "dogfood", "README.md"));
|
|
127
|
+
}
|
|
128
|
+
async function listJson(runGh, cwd, resource) {
|
|
129
|
+
const stdout = await runGh(
|
|
130
|
+
[
|
|
131
|
+
resource,
|
|
132
|
+
"list",
|
|
133
|
+
"--repo",
|
|
134
|
+
PUBLIC_INBOUND_REPO,
|
|
135
|
+
"--state",
|
|
136
|
+
"open",
|
|
137
|
+
"--limit",
|
|
138
|
+
"100",
|
|
139
|
+
"--json",
|
|
140
|
+
"number,title,author,updatedAt,labels,url"
|
|
141
|
+
],
|
|
142
|
+
{ cwd }
|
|
143
|
+
);
|
|
144
|
+
const trimmed = stdout.trim();
|
|
145
|
+
if (!trimmed) return [];
|
|
146
|
+
const parsed = JSON.parse(trimmed);
|
|
147
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
148
|
+
}
|
|
149
|
+
function resultBase(status, message, factory, now, items = []) {
|
|
150
|
+
return {
|
|
151
|
+
status,
|
|
152
|
+
repo: PUBLIC_INBOUND_REPO,
|
|
153
|
+
checkedAt: now.toISOString(),
|
|
154
|
+
factory,
|
|
155
|
+
items,
|
|
156
|
+
summary: summarize(items),
|
|
157
|
+
message,
|
|
158
|
+
mutateRecommended: false
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
async function checkPublicInbound(cwd, options = {}) {
|
|
162
|
+
const now = options.now?.() ?? /* @__PURE__ */ new Date();
|
|
163
|
+
const runGh = options.runGh ?? defaultRunGh;
|
|
164
|
+
const isFactory = options.isFactory ?? detectFactoryCheckout;
|
|
165
|
+
const root = path.resolve(cwd);
|
|
166
|
+
try {
|
|
167
|
+
await access(root);
|
|
168
|
+
} catch {
|
|
169
|
+
return resultBase("error", `cwd unreadable: ${root}`, false, now);
|
|
170
|
+
}
|
|
171
|
+
const factory = await isFactory(root);
|
|
172
|
+
if (!factory) {
|
|
173
|
+
return resultBase(
|
|
174
|
+
"skipped-non-factory",
|
|
175
|
+
"Factory-only check. Aborting outside agent-kit-dev / dogfood checkout.",
|
|
176
|
+
false,
|
|
177
|
+
now
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
if (options.respectPrefs) {
|
|
181
|
+
const prefs = readPublicInboundCheckPrefs(await loadContextConfig(root));
|
|
182
|
+
if (!prefs.enabled) {
|
|
183
|
+
return resultBase(
|
|
184
|
+
"skipped-disabled",
|
|
185
|
+
"publicInboundCheck.enabled is false (opt-in). Set true in .cursor/context/config.json to nudge.",
|
|
186
|
+
true,
|
|
187
|
+
now
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
if (!intervalElapsed(prefs.lastCheckedAt, prefs.intervalDays)) {
|
|
191
|
+
return resultBase(
|
|
192
|
+
"skipped-interval",
|
|
193
|
+
`Within publicInboundCheck.intervalDays (${prefs.intervalDays}); last check ${prefs.lastCheckedAt}.`,
|
|
194
|
+
true,
|
|
195
|
+
now
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
if (options.offline) {
|
|
200
|
+
return resultBase("skipped-offline", "Offline flag set; skipped GitHub fetch.", true, now);
|
|
201
|
+
}
|
|
202
|
+
if (!await ghAvailable(runGh, root)) {
|
|
203
|
+
return resultBase(
|
|
204
|
+
"skipped-gh-missing",
|
|
205
|
+
"gh CLI missing or not runnable; fail-open skip.",
|
|
206
|
+
true,
|
|
207
|
+
now
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
try {
|
|
211
|
+
const [issueRows, prRows] = await Promise.all([
|
|
212
|
+
listJson(runGh, root, "issue"),
|
|
213
|
+
listJson(runGh, root, "pr")
|
|
214
|
+
]);
|
|
215
|
+
const items = [...mapRows("issue", issueRows), ...mapRows("pull_request", prRows)].sort(
|
|
216
|
+
(a, b) => b.number - a.number
|
|
217
|
+
);
|
|
218
|
+
const summary = summarize(items);
|
|
219
|
+
const message = summary.total === 0 ? `No open issues or pull requests on ${PUBLIC_INBOUND_REPO}.` : `Open inbound on ${PUBLIC_INBOUND_REPO}: ${summary.issues} issue(s), ${summary.humanPullRequests} human PR(s), ${summary.dependabotPullRequests} Dependabot PR(s).`;
|
|
220
|
+
const result = resultBase("ok", message, true, now, items);
|
|
221
|
+
if (options.stamp) {
|
|
222
|
+
await stampPublicInboundCheck(root, now);
|
|
223
|
+
}
|
|
224
|
+
return result;
|
|
225
|
+
} catch (err) {
|
|
226
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
227
|
+
if (/ENOTFOUND|ECONNREFUSED|ETIMEDOUT|network|offline|Could not resolve/i.test(detail)) {
|
|
228
|
+
return resultBase(
|
|
229
|
+
"skipped-offline",
|
|
230
|
+
`GitHub unreachable; fail-open skip (${detail}).`,
|
|
231
|
+
true,
|
|
232
|
+
now
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
return resultBase("error", `Public inbound check failed: ${detail}`, true, now);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
function shouldEmitPublicInboundNudge(result) {
|
|
239
|
+
if (!result || result.status !== "ok") return false;
|
|
240
|
+
if (result.mutateRecommended === true) return false;
|
|
241
|
+
const summary = result.summary;
|
|
242
|
+
if (!summary || typeof summary !== "object") return false;
|
|
243
|
+
const total = summary.total;
|
|
244
|
+
return typeof total === "number" && total > 0;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export {
|
|
248
|
+
PUBLIC_INBOUND_REPO,
|
|
249
|
+
stampPublicInboundCheck,
|
|
250
|
+
detectFactoryCheckout,
|
|
251
|
+
checkPublicInbound,
|
|
252
|
+
shouldEmitPublicInboundNudge
|
|
253
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// src/invariants/handoff-schema.ts
|
|
2
|
+
var MACHINE_LIST_CHECKS = [
|
|
3
|
+
{
|
|
4
|
+
heading: "Backlog plans",
|
|
5
|
+
fieldLabels: ["Backlog plans", "Backlog"],
|
|
6
|
+
code: "heading-without-field-backlog"
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
heading: "Parked plans",
|
|
10
|
+
fieldLabels: ["Parked plans"],
|
|
11
|
+
code: "heading-without-field-parked"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
heading: "Run queue",
|
|
15
|
+
fieldLabels: ["Run queue"],
|
|
16
|
+
code: "heading-without-field-run-queue"
|
|
17
|
+
}
|
|
18
|
+
];
|
|
19
|
+
var CITE = "agent-kit validate handoff (see .cursor/context/templates/handoff.md)";
|
|
20
|
+
function validateHandoffText(text) {
|
|
21
|
+
if (!text.trim()) return [];
|
|
22
|
+
const warnings = [];
|
|
23
|
+
for (const check of MACHINE_LIST_CHECKS) {
|
|
24
|
+
const headingRe = new RegExp(`^##\\s+${check.heading}\\s*$`, "m");
|
|
25
|
+
if (!headingRe.test(text)) continue;
|
|
26
|
+
const hasField = check.fieldLabels.some(
|
|
27
|
+
(label) => new RegExp(`^- \\*\\*${label}:\\*\\*`, "m").test(text)
|
|
28
|
+
);
|
|
29
|
+
if (!hasField) {
|
|
30
|
+
warnings.push({
|
|
31
|
+
code: check.code,
|
|
32
|
+
message: `\`## ${check.heading}\` found without \`- **${check.fieldLabels[0]}:**\` (Mission Control will miss this list; rewrite as a field bullet).`,
|
|
33
|
+
cite: CITE
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return warnings;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export {
|
|
41
|
+
validateHandoffText
|
|
42
|
+
};
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
// src/readiness/env-checks.ts
|
|
2
|
+
import { constants, access, readFile, realpath, stat } from "fs/promises";
|
|
3
|
+
import { homedir } from "os";
|
|
4
|
+
import path from "path";
|
|
5
|
+
var MIN_NODE_MAJOR = 20;
|
|
6
|
+
var CLI_PACKAGE_JSON_REL = path.join(
|
|
7
|
+
"lib",
|
|
8
|
+
"node_modules",
|
|
9
|
+
"@dadado",
|
|
10
|
+
"agent-kit-cli",
|
|
11
|
+
"package.json"
|
|
12
|
+
);
|
|
13
|
+
var DIST_INDEX_RE = /(?:\$\{basedir\}|\$basedir)?[^'"\s;]*dist\/index\.js/g;
|
|
14
|
+
function extractDistIndexFromBinScript(contents) {
|
|
15
|
+
const found = [];
|
|
16
|
+
DIST_INDEX_RE.lastIndex = 0;
|
|
17
|
+
for (const match of contents.matchAll(DIST_INDEX_RE)) {
|
|
18
|
+
const raw = match[0]?.trim();
|
|
19
|
+
if (raw) found.push(raw.replace(/\\/g, "/"));
|
|
20
|
+
}
|
|
21
|
+
return found;
|
|
22
|
+
}
|
|
23
|
+
function expandBinDirVars(raw, binDir) {
|
|
24
|
+
return raw.replace(/\$\{basedir\}/g, binDir).replace(/\$basedir/g, binDir);
|
|
25
|
+
}
|
|
26
|
+
var SHIM_MAX_BYTES = 4096;
|
|
27
|
+
function packageJsonFromDistIndex(distIndex) {
|
|
28
|
+
return path.join(path.dirname(distIndex), "..", "package.json");
|
|
29
|
+
}
|
|
30
|
+
function parsePackageVersion(contents) {
|
|
31
|
+
try {
|
|
32
|
+
const parsed = JSON.parse(contents);
|
|
33
|
+
return typeof parsed.version === "string" && parsed.version.length > 0 ? parsed.version : null;
|
|
34
|
+
} catch {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
async function readBinVersion(binPath, options = {}) {
|
|
39
|
+
const readFileImpl = options.readFileImpl ?? ((filePath) => readFile(filePath, "utf8"));
|
|
40
|
+
const binDir = path.dirname(binPath);
|
|
41
|
+
const candidates = [];
|
|
42
|
+
let realBin = binPath;
|
|
43
|
+
try {
|
|
44
|
+
realBin = await realpath(binPath);
|
|
45
|
+
} catch {
|
|
46
|
+
}
|
|
47
|
+
if (path.basename(realBin) === "index.js" && path.basename(path.dirname(realBin)) === "dist") {
|
|
48
|
+
candidates.push(packageJsonFromDistIndex(realBin));
|
|
49
|
+
}
|
|
50
|
+
candidates.push(
|
|
51
|
+
path.join(binDir, "..", CLI_PACKAGE_JSON_REL),
|
|
52
|
+
path.join(binDir, "..", "node_modules", "@dadado", "agent-kit-cli", "package.json")
|
|
53
|
+
);
|
|
54
|
+
try {
|
|
55
|
+
if ((await stat(realBin)).size < SHIM_MAX_BYTES) {
|
|
56
|
+
const contents = await readFileImpl(binPath);
|
|
57
|
+
for (const raw of extractDistIndexFromBinScript(contents)) {
|
|
58
|
+
const distIndex = path.resolve(binDir, expandBinDirVars(raw, binDir));
|
|
59
|
+
candidates.push(packageJsonFromDistIndex(distIndex));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
} catch {
|
|
63
|
+
}
|
|
64
|
+
const seen = /* @__PURE__ */ new Set();
|
|
65
|
+
for (const candidate of candidates) {
|
|
66
|
+
const resolved = path.resolve(candidate);
|
|
67
|
+
if (seen.has(resolved)) continue;
|
|
68
|
+
seen.add(resolved);
|
|
69
|
+
try {
|
|
70
|
+
const version = parsePackageVersion(await readFileImpl(resolved));
|
|
71
|
+
if (version) return version;
|
|
72
|
+
} catch {
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
async function resolveBinOnPath(binName, env, platform) {
|
|
78
|
+
const pathVar = env.PATH ?? env.Path ?? "";
|
|
79
|
+
if (!pathVar) return null;
|
|
80
|
+
const dirs = pathVar.split(path.delimiter).filter(Boolean);
|
|
81
|
+
const candidates = platform === "win32" ? [binName, `${binName}.cmd`, `${binName}.exe`, `${binName}.bat`] : [binName];
|
|
82
|
+
for (const dir of dirs) {
|
|
83
|
+
for (const candidate of candidates) {
|
|
84
|
+
const abs = path.join(dir, candidate);
|
|
85
|
+
try {
|
|
86
|
+
await access(abs, platform === "win32" ? void 0 : constants.X_OK);
|
|
87
|
+
return abs;
|
|
88
|
+
} catch {
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
function isNodeVersionOk(nodeVersion, minMajor = MIN_NODE_MAJOR) {
|
|
95
|
+
const match = /^v?(\d+)/.exec(nodeVersion);
|
|
96
|
+
if (!match) return false;
|
|
97
|
+
const major = Number(match[1]);
|
|
98
|
+
return Number.isFinite(major) && major >= minMajor;
|
|
99
|
+
}
|
|
100
|
+
function detectShellName(env, platform) {
|
|
101
|
+
if (platform === "win32") {
|
|
102
|
+
if (env.PSModulePath) return "powershell";
|
|
103
|
+
if (env.ComSpec) return "cmd";
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
const shellPath = env.SHELL;
|
|
107
|
+
if (!shellPath) return null;
|
|
108
|
+
const base = path.basename(shellPath).trim();
|
|
109
|
+
return base || null;
|
|
110
|
+
}
|
|
111
|
+
function detectShellProfile(env, platform, homeDir) {
|
|
112
|
+
const shellName = detectShellName(env, platform);
|
|
113
|
+
if (shellName === "zsh") return path.join(homeDir, ".zshrc");
|
|
114
|
+
if (shellName === "bash") return path.join(homeDir, ".bashrc");
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
function parseNpmrcPrefix(content, homeDir) {
|
|
118
|
+
const match = /^\s*prefix\s*=\s*(.+?)\s*$/m.exec(content);
|
|
119
|
+
const captured = match?.[1];
|
|
120
|
+
if (!captured) return null;
|
|
121
|
+
let value = captured.trim().replace(/^["']|["']$/g, "");
|
|
122
|
+
if (value.startsWith("~")) {
|
|
123
|
+
value = path.join(homeDir, value.slice(1));
|
|
124
|
+
}
|
|
125
|
+
return value || null;
|
|
126
|
+
}
|
|
127
|
+
function heuristicPrefixFromExecPath(execPath, platform) {
|
|
128
|
+
const p = platform === "win32" ? path.win32 : path.posix;
|
|
129
|
+
return platform === "win32" ? p.dirname(execPath) : p.dirname(p.dirname(execPath));
|
|
130
|
+
}
|
|
131
|
+
async function detectNpmPrefix(options = {}) {
|
|
132
|
+
const env = options.env ?? process.env;
|
|
133
|
+
const platform = options.platform ?? process.platform;
|
|
134
|
+
const homeDir = options.homeDir ?? homedir();
|
|
135
|
+
const execPath = options.execPath ?? process.execPath;
|
|
136
|
+
const readFileImpl = options.readFileImpl ?? ((filePath) => readFile(filePath, "utf8"));
|
|
137
|
+
const envPrefix = env.npm_config_prefix ?? env.NPM_CONFIG_PREFIX;
|
|
138
|
+
if (envPrefix?.trim()) {
|
|
139
|
+
return { prefix: envPrefix.trim(), source: "env" };
|
|
140
|
+
}
|
|
141
|
+
const userconfigPath = env.NPM_CONFIG_USERCONFIG ?? path.join(homeDir, ".npmrc");
|
|
142
|
+
try {
|
|
143
|
+
const content = await readFileImpl(userconfigPath);
|
|
144
|
+
const npmrcPrefix = parseNpmrcPrefix(content, homeDir);
|
|
145
|
+
if (npmrcPrefix) {
|
|
146
|
+
return { prefix: npmrcPrefix, source: "npmrc" };
|
|
147
|
+
}
|
|
148
|
+
} catch {
|
|
149
|
+
}
|
|
150
|
+
return { prefix: heuristicPrefixFromExecPath(execPath, platform), source: "heuristic" };
|
|
151
|
+
}
|
|
152
|
+
async function describeUnwritablePrefix(prefix) {
|
|
153
|
+
try {
|
|
154
|
+
const info = await stat(prefix);
|
|
155
|
+
const currentUid = typeof process.getuid === "function" ? process.getuid() : void 0;
|
|
156
|
+
if (process.platform !== "win32" && currentUid !== void 0 && info.uid === 0 && currentUid !== 0) {
|
|
157
|
+
return `root-owned prefix (${prefix}); the classic fresh-install PATH/EACCES blocker`;
|
|
158
|
+
}
|
|
159
|
+
} catch {
|
|
160
|
+
}
|
|
161
|
+
return `npm prefix is not writable: ${prefix}`;
|
|
162
|
+
}
|
|
163
|
+
async function checkNpmPrefixWritable(options = {}) {
|
|
164
|
+
let detected;
|
|
165
|
+
try {
|
|
166
|
+
detected = await detectNpmPrefix(options);
|
|
167
|
+
} catch {
|
|
168
|
+
return { prefix: null, writable: false, reason: "npm prefix could not be determined" };
|
|
169
|
+
}
|
|
170
|
+
try {
|
|
171
|
+
await access(detected.prefix, constants.W_OK);
|
|
172
|
+
return { prefix: detected.prefix, writable: true, source: detected.source };
|
|
173
|
+
} catch {
|
|
174
|
+
return {
|
|
175
|
+
prefix: detected.prefix,
|
|
176
|
+
writable: false,
|
|
177
|
+
source: detected.source,
|
|
178
|
+
reason: await describeUnwritablePrefix(detected.prefix)
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
async function assessEnvironment(options = {}) {
|
|
183
|
+
const env = options.env ?? process.env;
|
|
184
|
+
const platform = options.platform ?? process.platform;
|
|
185
|
+
const nodeVersion = options.nodeVersion ?? process.version;
|
|
186
|
+
const homeDir = options.homeDir ?? homedir();
|
|
187
|
+
const binName = options.binName ?? "agent-kit";
|
|
188
|
+
const [binPath, npmPrefix] = await Promise.all([
|
|
189
|
+
resolveBinOnPath(binName, env, platform).catch(() => null),
|
|
190
|
+
checkNpmPrefixWritable(options).catch(
|
|
191
|
+
() => ({
|
|
192
|
+
prefix: null,
|
|
193
|
+
writable: false,
|
|
194
|
+
reason: "npm prefix check failed unexpectedly"
|
|
195
|
+
})
|
|
196
|
+
)
|
|
197
|
+
]);
|
|
198
|
+
const binVersion = binPath ? await readBinVersion(binPath, options).catch(() => null) : null;
|
|
199
|
+
return {
|
|
200
|
+
binOnPath: binPath != null,
|
|
201
|
+
binPath,
|
|
202
|
+
binVersion,
|
|
203
|
+
npmPrefixWritable: npmPrefix.writable,
|
|
204
|
+
npmPrefix,
|
|
205
|
+
nodeVersionOk: isNodeVersionOk(nodeVersion),
|
|
206
|
+
nodeVersion,
|
|
207
|
+
shell: detectShellName(env, platform),
|
|
208
|
+
shellProfile: detectShellProfile(env, platform, homeDir)
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export {
|
|
213
|
+
assessEnvironment
|
|
214
|
+
};
|