opencode-swarm 7.68.1 → 7.68.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +218 -234
- package/dist/index.js +218 -234
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -52,7 +52,7 @@ var package_default;
|
|
|
52
52
|
var init_package = __esm(() => {
|
|
53
53
|
package_default = {
|
|
54
54
|
name: "opencode-swarm",
|
|
55
|
-
version: "7.68.
|
|
55
|
+
version: "7.68.2",
|
|
56
56
|
description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
57
57
|
main: "dist/index.js",
|
|
58
58
|
types: "dist/index.d.ts",
|
|
@@ -49731,7 +49731,7 @@ var init_history = __esm(() => {
|
|
|
49731
49731
|
init_history_service();
|
|
49732
49732
|
});
|
|
49733
49733
|
|
|
49734
|
-
// src/commands/
|
|
49734
|
+
// src/commands/pr-ref.ts
|
|
49735
49735
|
import { execSync as execSync2 } from "child_process";
|
|
49736
49736
|
function sanitizeUrl(raw) {
|
|
49737
49737
|
let urlStr = raw.trim();
|
|
@@ -49750,6 +49750,22 @@ function sanitizeUrl(raw) {
|
|
|
49750
49750
|
}
|
|
49751
49751
|
return urlStr.trim();
|
|
49752
49752
|
}
|
|
49753
|
+
function sanitizeInstructions(raw) {
|
|
49754
|
+
const collapsed = raw.replace(/\s+/g, " ").trim();
|
|
49755
|
+
const stripped = collapsed.replace(/\[\s*MODE\s*:[^\]]*\]/gi, "");
|
|
49756
|
+
const normalized = stripped.replace(/\s+/g, " ").trim();
|
|
49757
|
+
if (normalized.length <= MAX_INSTRUCTIONS_LEN)
|
|
49758
|
+
return normalized;
|
|
49759
|
+
return `${normalized.slice(0, MAX_INSTRUCTIONS_LEN)}\u2026`;
|
|
49760
|
+
}
|
|
49761
|
+
function hasNonAsciiHostname(hostname5) {
|
|
49762
|
+
for (const ch of hostname5) {
|
|
49763
|
+
const cp = ch.codePointAt(0);
|
|
49764
|
+
if (cp !== undefined && cp > 127)
|
|
49765
|
+
return true;
|
|
49766
|
+
}
|
|
49767
|
+
return false;
|
|
49768
|
+
}
|
|
49753
49769
|
function isPrivateHost(url3) {
|
|
49754
49770
|
const host = url3.hostname.toLowerCase();
|
|
49755
49771
|
if (host === "localhost" || host === "127.0.0.1" || host === "::1" || host === "0.0.0.0") {
|
|
@@ -49782,13 +49798,182 @@ function validateAndSanitizeUrl(rawUrl) {
|
|
|
49782
49798
|
if (!sanitized.startsWith("https://")) {
|
|
49783
49799
|
return { error: "URL must use HTTPS scheme" };
|
|
49784
49800
|
}
|
|
49801
|
+
try {
|
|
49802
|
+
const url3 = new URL(sanitized);
|
|
49803
|
+
if (hasNonAsciiHostname(url3.hostname)) {
|
|
49804
|
+
return { error: "Non-ASCII hostnames are not allowed" };
|
|
49805
|
+
}
|
|
49806
|
+
if (isPrivateHost(url3)) {
|
|
49807
|
+
return { error: "Private or localhost URLs are not allowed" };
|
|
49808
|
+
}
|
|
49809
|
+
const githubPrPattern = /^https:\/\/github\.com\/([^/]+)\/([^/]+)\/pull\/([0-9]+)\/?$/;
|
|
49810
|
+
if (!githubPrPattern.test(sanitized)) {
|
|
49811
|
+
return {
|
|
49812
|
+
error: "URL must be a GitHub pull request URL (https://github.com/owner/repo/pull/N)"
|
|
49813
|
+
};
|
|
49814
|
+
}
|
|
49815
|
+
return { sanitized };
|
|
49816
|
+
} catch {
|
|
49817
|
+
return { error: "Invalid URL format" };
|
|
49818
|
+
}
|
|
49819
|
+
}
|
|
49820
|
+
function parsePrRef(input, cwd) {
|
|
49821
|
+
const urlMatch = input.match(/^https:\/\/github\.com\/([^/]+)\/([^/]+)\/pull\/(\d+)\/?$/i);
|
|
49822
|
+
if (urlMatch) {
|
|
49823
|
+
return {
|
|
49824
|
+
owner: urlMatch[1],
|
|
49825
|
+
repo: urlMatch[2],
|
|
49826
|
+
number: parseInt(urlMatch[3], 10)
|
|
49827
|
+
};
|
|
49828
|
+
}
|
|
49829
|
+
const shorthandMatch = input.match(/^([^/]+)\/([^#]+)#(\d+)$/);
|
|
49830
|
+
if (shorthandMatch) {
|
|
49831
|
+
return {
|
|
49832
|
+
owner: shorthandMatch[1],
|
|
49833
|
+
repo: shorthandMatch[2],
|
|
49834
|
+
number: parseInt(shorthandMatch[3], 10)
|
|
49835
|
+
};
|
|
49836
|
+
}
|
|
49837
|
+
const bareMatch = input.match(/^(\d+)$/);
|
|
49838
|
+
if (bareMatch) {
|
|
49839
|
+
const prNumber = parseInt(bareMatch[1], 10);
|
|
49840
|
+
const remoteUrl = detectGitRemote(cwd);
|
|
49841
|
+
if (!remoteUrl) {
|
|
49842
|
+
return null;
|
|
49843
|
+
}
|
|
49844
|
+
const parsed = parseGitRemoteUrl(remoteUrl);
|
|
49845
|
+
if (!parsed) {
|
|
49846
|
+
return null;
|
|
49847
|
+
}
|
|
49848
|
+
return {
|
|
49849
|
+
owner: parsed.owner,
|
|
49850
|
+
repo: parsed.repo,
|
|
49851
|
+
number: prNumber
|
|
49852
|
+
};
|
|
49853
|
+
}
|
|
49854
|
+
return null;
|
|
49855
|
+
}
|
|
49856
|
+
function detectGitRemote(cwd) {
|
|
49857
|
+
try {
|
|
49858
|
+
const remoteUrl = _internals23.execSync("git remote get-url origin", {
|
|
49859
|
+
encoding: "utf-8",
|
|
49860
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
49861
|
+
timeout: 5000,
|
|
49862
|
+
...cwd ? { cwd } : {}
|
|
49863
|
+
}).trim();
|
|
49864
|
+
return remoteUrl || null;
|
|
49865
|
+
} catch {
|
|
49866
|
+
return null;
|
|
49867
|
+
}
|
|
49868
|
+
}
|
|
49869
|
+
function parseGitRemoteUrl(remoteUrl) {
|
|
49870
|
+
const httpsMatch = remoteUrl.match(/^https:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?\/?$/i);
|
|
49871
|
+
if (httpsMatch) {
|
|
49872
|
+
return {
|
|
49873
|
+
owner: httpsMatch[1],
|
|
49874
|
+
repo: httpsMatch[2].replace(/\.git$/, "")
|
|
49875
|
+
};
|
|
49876
|
+
}
|
|
49877
|
+
const sshMatch = remoteUrl.match(/^git@github\.com:([^/]+)\/([^/]+?)(?:\.git)?$/i);
|
|
49878
|
+
if (sshMatch) {
|
|
49879
|
+
return {
|
|
49880
|
+
owner: sshMatch[1],
|
|
49881
|
+
repo: sshMatch[2].replace(/\.git$/, "")
|
|
49882
|
+
};
|
|
49883
|
+
}
|
|
49884
|
+
const pathMatch = remoteUrl.match(/\/([^/]+)\/([^/]+?)(?:\.git)?\/?$/);
|
|
49885
|
+
if (pathMatch) {
|
|
49886
|
+
return {
|
|
49887
|
+
owner: pathMatch[1],
|
|
49888
|
+
repo: pathMatch[2].replace(/\.git$/, "")
|
|
49889
|
+
};
|
|
49890
|
+
}
|
|
49891
|
+
return null;
|
|
49892
|
+
}
|
|
49893
|
+
function looksLikePrRef(token) {
|
|
49894
|
+
return /^https?:\/\//i.test(token) || /^[^/]+\/[^#]+#\d+$/.test(token) || /^\d+$/.test(token);
|
|
49895
|
+
}
|
|
49896
|
+
function resolvePrCommandInput(rest, cwd) {
|
|
49897
|
+
if (rest.length === 0) {
|
|
49898
|
+
return null;
|
|
49899
|
+
}
|
|
49900
|
+
const refToken = rest[0];
|
|
49901
|
+
const instructions = sanitizeInstructions(rest.slice(1).join(" "));
|
|
49902
|
+
const isFullUrl = /^https?:\/\//i.test(refToken);
|
|
49903
|
+
const prInfo = parsePrRef(isFullUrl ? sanitizeUrl(refToken) : refToken, cwd);
|
|
49904
|
+
if (!prInfo) {
|
|
49905
|
+
return { error: `Could not parse PR reference from "${refToken}"` };
|
|
49906
|
+
}
|
|
49907
|
+
const prUrl = `https://github.com/${prInfo.owner}/${prInfo.repo}/pull/${prInfo.number}`;
|
|
49908
|
+
const result = validateAndSanitizeUrl(prUrl);
|
|
49909
|
+
if ("error" in result) {
|
|
49910
|
+
return { error: result.error };
|
|
49911
|
+
}
|
|
49912
|
+
return { prUrl: result.sanitized, instructions };
|
|
49913
|
+
}
|
|
49914
|
+
var _internals23, MAX_URL_LEN = 2048, MAX_INSTRUCTIONS_LEN = 1000;
|
|
49915
|
+
var init_pr_ref = __esm(() => {
|
|
49916
|
+
_internals23 = { execSync: execSync2 };
|
|
49917
|
+
});
|
|
49918
|
+
|
|
49919
|
+
// src/commands/issue.ts
|
|
49920
|
+
import { execSync as execSync3 } from "child_process";
|
|
49921
|
+
function sanitizeUrl2(raw) {
|
|
49922
|
+
let urlStr = raw.trim();
|
|
49923
|
+
urlStr = urlStr.replace(/\[\s*MODE\s*:[^\]]*\]/gi, "");
|
|
49924
|
+
const fragmentIdx = urlStr.indexOf("#");
|
|
49925
|
+
if (fragmentIdx !== -1) {
|
|
49926
|
+
urlStr = urlStr.slice(0, fragmentIdx);
|
|
49927
|
+
}
|
|
49928
|
+
const queryIdx = urlStr.indexOf("?");
|
|
49929
|
+
if (queryIdx !== -1) {
|
|
49930
|
+
urlStr = urlStr.slice(0, queryIdx);
|
|
49931
|
+
}
|
|
49932
|
+
urlStr = urlStr.replace(/^[A-Za-z][A-Za-z0-9+.-]*:\/\/[^@/]+@/, "https://");
|
|
49933
|
+
if (urlStr.length > MAX_URL_LEN2) {
|
|
49934
|
+
urlStr = urlStr.slice(0, MAX_URL_LEN2);
|
|
49935
|
+
}
|
|
49936
|
+
return urlStr.trim();
|
|
49937
|
+
}
|
|
49938
|
+
function isPrivateHost2(url3) {
|
|
49939
|
+
const host = url3.hostname.toLowerCase();
|
|
49940
|
+
if (host === "localhost" || host === "127.0.0.1" || host === "::1" || host === "0.0.0.0") {
|
|
49941
|
+
return true;
|
|
49942
|
+
}
|
|
49943
|
+
if (host.startsWith("localhost") || host === "localhost.com") {
|
|
49944
|
+
return true;
|
|
49945
|
+
}
|
|
49946
|
+
const ipv4Private = /^10\./;
|
|
49947
|
+
const ipv4172 = /^172\.(1[6-9]|2\d|3[0-1])\./;
|
|
49948
|
+
const ipv4192 = /^192\.168\./;
|
|
49949
|
+
const ipv6Private = /^fe80:/i;
|
|
49950
|
+
const ipv6Unique = /^f[cd][0-9a-f]{2}:/i;
|
|
49951
|
+
if (ipv4Private.test(host) || ipv4172.test(host) || ipv4192.test(host) || ipv6Private.test(host) || ipv6Unique.test(host)) {
|
|
49952
|
+
return true;
|
|
49953
|
+
}
|
|
49954
|
+
if (host.startsWith("::ffff:")) {
|
|
49955
|
+
const inner = host.slice(7);
|
|
49956
|
+
if (ipv4Private.test(inner) || ipv4172.test(inner) || ipv4192.test(inner)) {
|
|
49957
|
+
return true;
|
|
49958
|
+
}
|
|
49959
|
+
}
|
|
49960
|
+
return false;
|
|
49961
|
+
}
|
|
49962
|
+
function validateAndSanitizeUrl2(rawUrl) {
|
|
49963
|
+
const sanitized = sanitizeUrl2(rawUrl);
|
|
49964
|
+
if (!sanitized) {
|
|
49965
|
+
return { error: "Empty URL" };
|
|
49966
|
+
}
|
|
49967
|
+
if (!sanitized.startsWith("https://")) {
|
|
49968
|
+
return { error: "URL must use HTTPS scheme" };
|
|
49969
|
+
}
|
|
49785
49970
|
try {
|
|
49786
49971
|
const url3 = new URL(sanitized);
|
|
49787
49972
|
const hostname5 = url3.hostname;
|
|
49788
49973
|
if (/[\u0080-\u{10FFFF}]/u.test(hostname5)) {
|
|
49789
49974
|
return { error: "Non-ASCII hostnames are not allowed" };
|
|
49790
49975
|
}
|
|
49791
|
-
if (
|
|
49976
|
+
if (isPrivateHost2(url3)) {
|
|
49792
49977
|
return { error: "Private or localhost URLs are not allowed" };
|
|
49793
49978
|
}
|
|
49794
49979
|
const githubIssuePattern = /^https:\/\/github\.com\/([^/]+)\/([^/]+)\/issues\/([0-9]+)\/?$/;
|
|
@@ -49847,7 +50032,7 @@ function parseIssueRef(input) {
|
|
|
49847
50032
|
const bareMatch = input.match(/^(\d+)$/);
|
|
49848
50033
|
if (bareMatch) {
|
|
49849
50034
|
const issueNumber = parseInt(bareMatch[1], 10);
|
|
49850
|
-
const remoteUrl =
|
|
50035
|
+
const remoteUrl = detectGitRemote2();
|
|
49851
50036
|
if (!remoteUrl) {
|
|
49852
50037
|
return null;
|
|
49853
50038
|
}
|
|
@@ -49863,9 +50048,9 @@ function parseIssueRef(input) {
|
|
|
49863
50048
|
}
|
|
49864
50049
|
return null;
|
|
49865
50050
|
}
|
|
49866
|
-
function
|
|
50051
|
+
function detectGitRemote2() {
|
|
49867
50052
|
try {
|
|
49868
|
-
const remoteUrl =
|
|
50053
|
+
const remoteUrl = execSync3("git remote get-url origin", {
|
|
49869
50054
|
encoding: "utf-8",
|
|
49870
50055
|
stdio: ["pipe", "pipe", "pipe"],
|
|
49871
50056
|
timeout: 5000
|
|
@@ -49875,23 +50060,6 @@ function detectGitRemote() {
|
|
|
49875
50060
|
return null;
|
|
49876
50061
|
}
|
|
49877
50062
|
}
|
|
49878
|
-
function parseGitRemoteUrl(remoteUrl) {
|
|
49879
|
-
const httpsMatch = remoteUrl.match(/^https:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?\/?$/i);
|
|
49880
|
-
if (httpsMatch) {
|
|
49881
|
-
return {
|
|
49882
|
-
owner: httpsMatch[1],
|
|
49883
|
-
repo: httpsMatch[2].replace(/\.git$/, "")
|
|
49884
|
-
};
|
|
49885
|
-
}
|
|
49886
|
-
const sshMatch = remoteUrl.match(/^git@github\.com:([^/]+)\/([^/]+?)(?:\.git)?$/i);
|
|
49887
|
-
if (sshMatch) {
|
|
49888
|
-
return {
|
|
49889
|
-
owner: sshMatch[1],
|
|
49890
|
-
repo: sshMatch[2].replace(/\.git$/, "")
|
|
49891
|
-
};
|
|
49892
|
-
}
|
|
49893
|
-
return null;
|
|
49894
|
-
}
|
|
49895
50063
|
function handleIssueCommand(_directory, args) {
|
|
49896
50064
|
const parsed = parseArgs5(args);
|
|
49897
50065
|
const rawInput = parsed.rest.join(" ").trim();
|
|
@@ -49899,14 +50067,14 @@ function handleIssueCommand(_directory, args) {
|
|
|
49899
50067
|
return USAGE5;
|
|
49900
50068
|
}
|
|
49901
50069
|
const isFullUrl = /^https?:\/\//i.test(rawInput);
|
|
49902
|
-
const issueInfo = parseIssueRef(isFullUrl ?
|
|
50070
|
+
const issueInfo = parseIssueRef(isFullUrl ? sanitizeUrl2(rawInput) : rawInput);
|
|
49903
50071
|
if (!issueInfo) {
|
|
49904
50072
|
return `Error: Could not parse issue reference from "${rawInput}"
|
|
49905
50073
|
|
|
49906
50074
|
${USAGE5}`;
|
|
49907
50075
|
}
|
|
49908
50076
|
const issueUrl = `https://github.com/${issueInfo.owner}/${issueInfo.repo}/issues/${issueInfo.number}`;
|
|
49909
|
-
const result =
|
|
50077
|
+
const result = validateAndSanitizeUrl2(issueUrl);
|
|
49910
50078
|
if ("error" in result) {
|
|
49911
50079
|
return `Error: ${result.error}
|
|
49912
50080
|
|
|
@@ -49922,8 +50090,9 @@ ${USAGE5}`;
|
|
|
49922
50090
|
const flagsStr = flags.length > 0 ? ` ${flags.join(" ")}` : "";
|
|
49923
50091
|
return `[MODE: ISSUE_INGEST issue="${result.sanitized}"${flagsStr}]`;
|
|
49924
50092
|
}
|
|
49925
|
-
var
|
|
50093
|
+
var MAX_URL_LEN2 = 2048, USAGE5;
|
|
49926
50094
|
var init_issue = __esm(() => {
|
|
50095
|
+
init_pr_ref();
|
|
49927
50096
|
USAGE5 = [
|
|
49928
50097
|
"Usage: /swarm issue <url|owner/repo#N|N> [--plan] [--trace] [--no-repro]",
|
|
49929
50098
|
"",
|
|
@@ -49991,9 +50160,9 @@ async function migrateContextToKnowledge(directory, config3) {
|
|
|
49991
50160
|
skippedReason: "empty-context"
|
|
49992
50161
|
};
|
|
49993
50162
|
}
|
|
49994
|
-
const rawEntries =
|
|
50163
|
+
const rawEntries = _internals24.parseContextMd(contextContent);
|
|
49995
50164
|
if (rawEntries.length === 0) {
|
|
49996
|
-
await
|
|
50165
|
+
await _internals24.writeSentinel(sentinelPath, 0, 0);
|
|
49997
50166
|
return {
|
|
49998
50167
|
migrated: true,
|
|
49999
50168
|
entriesMigrated: 0,
|
|
@@ -50004,10 +50173,10 @@ async function migrateContextToKnowledge(directory, config3) {
|
|
|
50004
50173
|
const existing = await readKnowledge(knowledgePath);
|
|
50005
50174
|
let migrated = 0;
|
|
50006
50175
|
let dropped = 0;
|
|
50007
|
-
const projectName =
|
|
50176
|
+
const projectName = _internals24.inferProjectName(directory);
|
|
50008
50177
|
for (const raw of rawEntries) {
|
|
50009
50178
|
if (config3.validation_enabled !== false) {
|
|
50010
|
-
const category = raw.categoryHint ??
|
|
50179
|
+
const category = raw.categoryHint ?? _internals24.inferCategoryFromText(raw.text);
|
|
50011
50180
|
const result = validateLesson(raw.text, existing.map((e) => e.lesson), {
|
|
50012
50181
|
category,
|
|
50013
50182
|
scope: "global",
|
|
@@ -50027,8 +50196,8 @@ async function migrateContextToKnowledge(directory, config3) {
|
|
|
50027
50196
|
const entry = {
|
|
50028
50197
|
id: randomUUID3(),
|
|
50029
50198
|
tier: "swarm",
|
|
50030
|
-
lesson:
|
|
50031
|
-
category: raw.categoryHint ??
|
|
50199
|
+
lesson: _internals24.truncateLesson(raw.text),
|
|
50200
|
+
category: raw.categoryHint ?? _internals24.inferCategoryFromText(raw.text),
|
|
50032
50201
|
tags: [...inferredTags, `migration:${raw.sourceSection}`],
|
|
50033
50202
|
scope: "global",
|
|
50034
50203
|
confidence: 0.3,
|
|
@@ -50051,7 +50220,7 @@ async function migrateContextToKnowledge(directory, config3) {
|
|
|
50051
50220
|
if (migrated > 0) {
|
|
50052
50221
|
await rewriteKnowledge(knowledgePath, existing);
|
|
50053
50222
|
}
|
|
50054
|
-
await
|
|
50223
|
+
await _internals24.writeSentinel(sentinelPath, migrated, dropped);
|
|
50055
50224
|
log(`[knowledge-migrator] Migrated ${migrated} entries, dropped ${dropped}`);
|
|
50056
50225
|
return {
|
|
50057
50226
|
migrated: true,
|
|
@@ -50061,7 +50230,7 @@ async function migrateContextToKnowledge(directory, config3) {
|
|
|
50061
50230
|
};
|
|
50062
50231
|
}
|
|
50063
50232
|
async function migrateHiveKnowledgeLegacy(config3) {
|
|
50064
|
-
const legacyHivePath =
|
|
50233
|
+
const legacyHivePath = _internals24.resolveLegacyHiveKnowledgePath();
|
|
50065
50234
|
const canonicalHivePath = resolveHiveKnowledgePath();
|
|
50066
50235
|
const sentinelPath = path37.join(path37.dirname(canonicalHivePath), ".hive-knowledge-migrated");
|
|
50067
50236
|
if (existsSync25(sentinelPath)) {
|
|
@@ -50084,7 +50253,7 @@ async function migrateHiveKnowledgeLegacy(config3) {
|
|
|
50084
50253
|
}
|
|
50085
50254
|
const legacyEntries = await readKnowledge(legacyHivePath);
|
|
50086
50255
|
if (legacyEntries.length === 0) {
|
|
50087
|
-
await
|
|
50256
|
+
await _internals24.writeSentinel(sentinelPath, 0, 0);
|
|
50088
50257
|
return {
|
|
50089
50258
|
migrated: true,
|
|
50090
50259
|
entriesMigrated: 0,
|
|
@@ -50132,7 +50301,7 @@ async function migrateHiveKnowledgeLegacy(config3) {
|
|
|
50132
50301
|
const newHiveEntry = {
|
|
50133
50302
|
id: resolvedId,
|
|
50134
50303
|
tier: "hive",
|
|
50135
|
-
lesson:
|
|
50304
|
+
lesson: _internals24.truncateLesson(lesson),
|
|
50136
50305
|
category,
|
|
50137
50306
|
tags: ["migration:legacy-hive"],
|
|
50138
50307
|
scope: scopeTag,
|
|
@@ -50151,7 +50320,7 @@ async function migrateHiveKnowledgeLegacy(config3) {
|
|
|
50151
50320
|
encounter_score: 1
|
|
50152
50321
|
};
|
|
50153
50322
|
try {
|
|
50154
|
-
await
|
|
50323
|
+
await _internals24.appendKnowledge(canonicalHivePath, newHiveEntry);
|
|
50155
50324
|
existingHiveEntries.push(newHiveEntry);
|
|
50156
50325
|
migrated++;
|
|
50157
50326
|
} catch (appendError) {
|
|
@@ -50167,7 +50336,7 @@ async function migrateHiveKnowledgeLegacy(config3) {
|
|
|
50167
50336
|
dropped++;
|
|
50168
50337
|
}
|
|
50169
50338
|
}
|
|
50170
|
-
await
|
|
50339
|
+
await _internals24.writeSentinel(sentinelPath, migrated, dropped);
|
|
50171
50340
|
log(`[knowledge-migrator] Migrated ${migrated} legacy hive entries, dropped ${dropped}`);
|
|
50172
50341
|
return {
|
|
50173
50342
|
migrated: true,
|
|
@@ -50178,7 +50347,7 @@ async function migrateHiveKnowledgeLegacy(config3) {
|
|
|
50178
50347
|
};
|
|
50179
50348
|
}
|
|
50180
50349
|
function parseContextMd(content) {
|
|
50181
|
-
const sections =
|
|
50350
|
+
const sections = _internals24.splitIntoSections(content);
|
|
50182
50351
|
const entries = [];
|
|
50183
50352
|
const seen = new Set;
|
|
50184
50353
|
const sectionPatterns = [
|
|
@@ -50194,7 +50363,7 @@ function parseContextMd(content) {
|
|
|
50194
50363
|
const match = sectionPatterns.find((sp) => sp.pattern.test(section.heading));
|
|
50195
50364
|
if (!match)
|
|
50196
50365
|
continue;
|
|
50197
|
-
const bullets =
|
|
50366
|
+
const bullets = _internals24.extractBullets(section.body);
|
|
50198
50367
|
for (const bullet of bullets) {
|
|
50199
50368
|
if (bullet.length < 15)
|
|
50200
50369
|
continue;
|
|
@@ -50203,9 +50372,9 @@ function parseContextMd(content) {
|
|
|
50203
50372
|
continue;
|
|
50204
50373
|
seen.add(normalized);
|
|
50205
50374
|
entries.push({
|
|
50206
|
-
text:
|
|
50375
|
+
text: _internals24.truncateLesson(bullet),
|
|
50207
50376
|
sourceSection: match.sourceSection,
|
|
50208
|
-
categoryHint:
|
|
50377
|
+
categoryHint: _internals24.inferCategoryFromText(bullet)
|
|
50209
50378
|
});
|
|
50210
50379
|
}
|
|
50211
50380
|
}
|
|
@@ -50311,12 +50480,12 @@ function resolveLegacyHiveKnowledgePath() {
|
|
|
50311
50480
|
}
|
|
50312
50481
|
return path37.join(dataDir, "hive-knowledge.jsonl");
|
|
50313
50482
|
}
|
|
50314
|
-
var
|
|
50483
|
+
var _internals24;
|
|
50315
50484
|
var init_knowledge_migrator = __esm(() => {
|
|
50316
50485
|
init_logger();
|
|
50317
50486
|
init_knowledge_store();
|
|
50318
50487
|
init_knowledge_validator();
|
|
50319
|
-
|
|
50488
|
+
_internals24 = {
|
|
50320
50489
|
appendKnowledge,
|
|
50321
50490
|
migrateContextToKnowledge,
|
|
50322
50491
|
migrateKnowledgeToExternal,
|
|
@@ -53833,9 +54002,9 @@ var init_memory2 = __esm(() => {
|
|
|
53833
54002
|
|
|
53834
54003
|
// src/services/plan-service.ts
|
|
53835
54004
|
async function getPlanData(directory, phaseArg) {
|
|
53836
|
-
const plan = await
|
|
54005
|
+
const plan = await _internals25.loadPlanJsonOnly(directory);
|
|
53837
54006
|
if (plan) {
|
|
53838
|
-
const fullMarkdown =
|
|
54007
|
+
const fullMarkdown = _internals25.derivePlanMarkdown(plan);
|
|
53839
54008
|
if (phaseArg === undefined || phaseArg === null || phaseArg === "") {
|
|
53840
54009
|
return {
|
|
53841
54010
|
hasPlan: true,
|
|
@@ -53878,7 +54047,7 @@ async function getPlanData(directory, phaseArg) {
|
|
|
53878
54047
|
isLegacy: false
|
|
53879
54048
|
};
|
|
53880
54049
|
}
|
|
53881
|
-
const planContent = await
|
|
54050
|
+
const planContent = await _internals25.readSwarmFileAsync(directory, "plan.md");
|
|
53882
54051
|
if (!planContent) {
|
|
53883
54052
|
return {
|
|
53884
54053
|
hasPlan: false,
|
|
@@ -53974,11 +54143,11 @@ async function handlePlanCommand(directory, args) {
|
|
|
53974
54143
|
const planData = await getPlanData(directory, phaseArg);
|
|
53975
54144
|
return formatPlanMarkdown(planData);
|
|
53976
54145
|
}
|
|
53977
|
-
var
|
|
54146
|
+
var _internals25;
|
|
53978
54147
|
var init_plan_service = __esm(() => {
|
|
53979
54148
|
init_utils2();
|
|
53980
54149
|
init_manager();
|
|
53981
|
-
|
|
54150
|
+
_internals25 = {
|
|
53982
54151
|
loadPlanJsonOnly,
|
|
53983
54152
|
derivePlanMarkdown,
|
|
53984
54153
|
readSwarmFileAsync
|
|
@@ -53990,191 +54159,6 @@ var init_plan = __esm(() => {
|
|
|
53990
54159
|
init_plan_service();
|
|
53991
54160
|
});
|
|
53992
54161
|
|
|
53993
|
-
// src/commands/pr-ref.ts
|
|
53994
|
-
import { execSync as execSync3 } from "child_process";
|
|
53995
|
-
function sanitizeUrl2(raw) {
|
|
53996
|
-
let urlStr = raw.trim();
|
|
53997
|
-
urlStr = urlStr.replace(/\[\s*MODE\s*:[^\]]*\]/gi, "");
|
|
53998
|
-
const fragmentIdx = urlStr.indexOf("#");
|
|
53999
|
-
if (fragmentIdx !== -1) {
|
|
54000
|
-
urlStr = urlStr.slice(0, fragmentIdx);
|
|
54001
|
-
}
|
|
54002
|
-
const queryIdx = urlStr.indexOf("?");
|
|
54003
|
-
if (queryIdx !== -1) {
|
|
54004
|
-
urlStr = urlStr.slice(0, queryIdx);
|
|
54005
|
-
}
|
|
54006
|
-
urlStr = urlStr.replace(/^[A-Za-z][A-Za-z0-9+.-]*:\/\/[^@/]+@/, "https://");
|
|
54007
|
-
if (urlStr.length > MAX_URL_LEN2) {
|
|
54008
|
-
urlStr = urlStr.slice(0, MAX_URL_LEN2);
|
|
54009
|
-
}
|
|
54010
|
-
return urlStr.trim();
|
|
54011
|
-
}
|
|
54012
|
-
function sanitizeInstructions(raw) {
|
|
54013
|
-
const collapsed = raw.replace(/\s+/g, " ").trim();
|
|
54014
|
-
const stripped = collapsed.replace(/\[\s*MODE\s*:[^\]]*\]/gi, "");
|
|
54015
|
-
const normalized = stripped.replace(/\s+/g, " ").trim();
|
|
54016
|
-
if (normalized.length <= MAX_INSTRUCTIONS_LEN)
|
|
54017
|
-
return normalized;
|
|
54018
|
-
return `${normalized.slice(0, MAX_INSTRUCTIONS_LEN)}\u2026`;
|
|
54019
|
-
}
|
|
54020
|
-
function hasNonAsciiHostname(hostname5) {
|
|
54021
|
-
for (const ch of hostname5) {
|
|
54022
|
-
const cp = ch.codePointAt(0);
|
|
54023
|
-
if (cp !== undefined && cp > 127)
|
|
54024
|
-
return true;
|
|
54025
|
-
}
|
|
54026
|
-
return false;
|
|
54027
|
-
}
|
|
54028
|
-
function isPrivateHost2(url3) {
|
|
54029
|
-
const host = url3.hostname.toLowerCase();
|
|
54030
|
-
if (host === "localhost" || host === "127.0.0.1" || host === "::1" || host === "0.0.0.0") {
|
|
54031
|
-
return true;
|
|
54032
|
-
}
|
|
54033
|
-
if (host.startsWith("localhost") || host === "localhost.com") {
|
|
54034
|
-
return true;
|
|
54035
|
-
}
|
|
54036
|
-
const ipv4Private = /^10\./;
|
|
54037
|
-
const ipv4172 = /^172\.(1[6-9]|2\d|3[0-1])\./;
|
|
54038
|
-
const ipv4192 = /^192\.168\./;
|
|
54039
|
-
const ipv6Private = /^fe80:/i;
|
|
54040
|
-
const ipv6Unique = /^f[cd][0-9a-f]{2}:/i;
|
|
54041
|
-
if (ipv4Private.test(host) || ipv4172.test(host) || ipv4192.test(host) || ipv6Private.test(host) || ipv6Unique.test(host)) {
|
|
54042
|
-
return true;
|
|
54043
|
-
}
|
|
54044
|
-
if (host.startsWith("::ffff:")) {
|
|
54045
|
-
const inner = host.slice(7);
|
|
54046
|
-
if (ipv4Private.test(inner) || ipv4172.test(inner) || ipv4192.test(inner)) {
|
|
54047
|
-
return true;
|
|
54048
|
-
}
|
|
54049
|
-
}
|
|
54050
|
-
return false;
|
|
54051
|
-
}
|
|
54052
|
-
function validateAndSanitizeUrl2(rawUrl) {
|
|
54053
|
-
const sanitized = sanitizeUrl2(rawUrl);
|
|
54054
|
-
if (!sanitized) {
|
|
54055
|
-
return { error: "Empty URL" };
|
|
54056
|
-
}
|
|
54057
|
-
if (!sanitized.startsWith("https://")) {
|
|
54058
|
-
return { error: "URL must use HTTPS scheme" };
|
|
54059
|
-
}
|
|
54060
|
-
try {
|
|
54061
|
-
const url3 = new URL(sanitized);
|
|
54062
|
-
if (hasNonAsciiHostname(url3.hostname)) {
|
|
54063
|
-
return { error: "Non-ASCII hostnames are not allowed" };
|
|
54064
|
-
}
|
|
54065
|
-
if (isPrivateHost2(url3)) {
|
|
54066
|
-
return { error: "Private or localhost URLs are not allowed" };
|
|
54067
|
-
}
|
|
54068
|
-
const githubPrPattern = /^https:\/\/github\.com\/([^/]+)\/([^/]+)\/pull\/([0-9]+)\/?$/;
|
|
54069
|
-
if (!githubPrPattern.test(sanitized)) {
|
|
54070
|
-
return {
|
|
54071
|
-
error: "URL must be a GitHub pull request URL (https://github.com/owner/repo/pull/N)"
|
|
54072
|
-
};
|
|
54073
|
-
}
|
|
54074
|
-
return { sanitized };
|
|
54075
|
-
} catch {
|
|
54076
|
-
return { error: "Invalid URL format" };
|
|
54077
|
-
}
|
|
54078
|
-
}
|
|
54079
|
-
function parsePrRef(input, cwd) {
|
|
54080
|
-
const urlMatch = input.match(/^https:\/\/github\.com\/([^/]+)\/([^/]+)\/pull\/(\d+)\/?$/i);
|
|
54081
|
-
if (urlMatch) {
|
|
54082
|
-
return {
|
|
54083
|
-
owner: urlMatch[1],
|
|
54084
|
-
repo: urlMatch[2],
|
|
54085
|
-
number: parseInt(urlMatch[3], 10)
|
|
54086
|
-
};
|
|
54087
|
-
}
|
|
54088
|
-
const shorthandMatch = input.match(/^([^/]+)\/([^#]+)#(\d+)$/);
|
|
54089
|
-
if (shorthandMatch) {
|
|
54090
|
-
return {
|
|
54091
|
-
owner: shorthandMatch[1],
|
|
54092
|
-
repo: shorthandMatch[2],
|
|
54093
|
-
number: parseInt(shorthandMatch[3], 10)
|
|
54094
|
-
};
|
|
54095
|
-
}
|
|
54096
|
-
const bareMatch = input.match(/^(\d+)$/);
|
|
54097
|
-
if (bareMatch) {
|
|
54098
|
-
const prNumber = parseInt(bareMatch[1], 10);
|
|
54099
|
-
const remoteUrl = detectGitRemote2(cwd);
|
|
54100
|
-
if (!remoteUrl) {
|
|
54101
|
-
return null;
|
|
54102
|
-
}
|
|
54103
|
-
const parsed = parseGitRemoteUrl2(remoteUrl);
|
|
54104
|
-
if (!parsed) {
|
|
54105
|
-
return null;
|
|
54106
|
-
}
|
|
54107
|
-
return {
|
|
54108
|
-
owner: parsed.owner,
|
|
54109
|
-
repo: parsed.repo,
|
|
54110
|
-
number: prNumber
|
|
54111
|
-
};
|
|
54112
|
-
}
|
|
54113
|
-
return null;
|
|
54114
|
-
}
|
|
54115
|
-
function detectGitRemote2(cwd) {
|
|
54116
|
-
try {
|
|
54117
|
-
const remoteUrl = _internals25.execSync("git remote get-url origin", {
|
|
54118
|
-
encoding: "utf-8",
|
|
54119
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
54120
|
-
timeout: 5000,
|
|
54121
|
-
...cwd ? { cwd } : {}
|
|
54122
|
-
}).trim();
|
|
54123
|
-
return remoteUrl || null;
|
|
54124
|
-
} catch {
|
|
54125
|
-
return null;
|
|
54126
|
-
}
|
|
54127
|
-
}
|
|
54128
|
-
function parseGitRemoteUrl2(remoteUrl) {
|
|
54129
|
-
const httpsMatch = remoteUrl.match(/^https:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?\/?$/i);
|
|
54130
|
-
if (httpsMatch) {
|
|
54131
|
-
return {
|
|
54132
|
-
owner: httpsMatch[1],
|
|
54133
|
-
repo: httpsMatch[2].replace(/\.git$/, "")
|
|
54134
|
-
};
|
|
54135
|
-
}
|
|
54136
|
-
const sshMatch = remoteUrl.match(/^git@github\.com:([^/]+)\/([^/]+?)(?:\.git)?$/i);
|
|
54137
|
-
if (sshMatch) {
|
|
54138
|
-
return {
|
|
54139
|
-
owner: sshMatch[1],
|
|
54140
|
-
repo: sshMatch[2].replace(/\.git$/, "")
|
|
54141
|
-
};
|
|
54142
|
-
}
|
|
54143
|
-
const pathMatch = remoteUrl.match(/\/([^/]+)\/([^/]+?)(?:\.git)?\/?$/);
|
|
54144
|
-
if (pathMatch) {
|
|
54145
|
-
return {
|
|
54146
|
-
owner: pathMatch[1],
|
|
54147
|
-
repo: pathMatch[2].replace(/\.git$/, "")
|
|
54148
|
-
};
|
|
54149
|
-
}
|
|
54150
|
-
return null;
|
|
54151
|
-
}
|
|
54152
|
-
function looksLikePrRef(token) {
|
|
54153
|
-
return /^https?:\/\//i.test(token) || /^[^/]+\/[^#]+#\d+$/.test(token) || /^\d+$/.test(token);
|
|
54154
|
-
}
|
|
54155
|
-
function resolvePrCommandInput(rest, cwd) {
|
|
54156
|
-
if (rest.length === 0) {
|
|
54157
|
-
return null;
|
|
54158
|
-
}
|
|
54159
|
-
const refToken = rest[0];
|
|
54160
|
-
const instructions = sanitizeInstructions(rest.slice(1).join(" "));
|
|
54161
|
-
const isFullUrl = /^https?:\/\//i.test(refToken);
|
|
54162
|
-
const prInfo = parsePrRef(isFullUrl ? sanitizeUrl2(refToken) : refToken, cwd);
|
|
54163
|
-
if (!prInfo) {
|
|
54164
|
-
return { error: `Could not parse PR reference from "${refToken}"` };
|
|
54165
|
-
}
|
|
54166
|
-
const prUrl = `https://github.com/${prInfo.owner}/${prInfo.repo}/pull/${prInfo.number}`;
|
|
54167
|
-
const result = validateAndSanitizeUrl2(prUrl);
|
|
54168
|
-
if ("error" in result) {
|
|
54169
|
-
return { error: result.error };
|
|
54170
|
-
}
|
|
54171
|
-
return { prUrl: result.sanitized, instructions };
|
|
54172
|
-
}
|
|
54173
|
-
var _internals25, MAX_URL_LEN2 = 2048, MAX_INSTRUCTIONS_LEN = 1000;
|
|
54174
|
-
var init_pr_ref = __esm(() => {
|
|
54175
|
-
_internals25 = { execSync: execSync3 };
|
|
54176
|
-
});
|
|
54177
|
-
|
|
54178
54162
|
// src/commands/pr-feedback.ts
|
|
54179
54163
|
function handlePrFeedbackCommand(directory, args) {
|
|
54180
54164
|
const rest = args.filter((t) => t.trim().length > 0);
|
package/dist/index.js
CHANGED
|
@@ -69,7 +69,7 @@ var package_default;
|
|
|
69
69
|
var init_package = __esm(() => {
|
|
70
70
|
package_default = {
|
|
71
71
|
name: "opencode-swarm",
|
|
72
|
-
version: "7.68.
|
|
72
|
+
version: "7.68.2",
|
|
73
73
|
description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
74
74
|
main: "dist/index.js",
|
|
75
75
|
types: "dist/index.d.ts",
|
|
@@ -73712,7 +73712,7 @@ var init_history = __esm(() => {
|
|
|
73712
73712
|
init_history_service();
|
|
73713
73713
|
});
|
|
73714
73714
|
|
|
73715
|
-
// src/commands/
|
|
73715
|
+
// src/commands/pr-ref.ts
|
|
73716
73716
|
import { execSync as execSync2 } from "node:child_process";
|
|
73717
73717
|
function sanitizeUrl(raw) {
|
|
73718
73718
|
let urlStr = raw.trim();
|
|
@@ -73731,6 +73731,22 @@ function sanitizeUrl(raw) {
|
|
|
73731
73731
|
}
|
|
73732
73732
|
return urlStr.trim();
|
|
73733
73733
|
}
|
|
73734
|
+
function sanitizeInstructions(raw) {
|
|
73735
|
+
const collapsed = raw.replace(/\s+/g, " ").trim();
|
|
73736
|
+
const stripped = collapsed.replace(/\[\s*MODE\s*:[^\]]*\]/gi, "");
|
|
73737
|
+
const normalized = stripped.replace(/\s+/g, " ").trim();
|
|
73738
|
+
if (normalized.length <= MAX_INSTRUCTIONS_LEN)
|
|
73739
|
+
return normalized;
|
|
73740
|
+
return `${normalized.slice(0, MAX_INSTRUCTIONS_LEN)}…`;
|
|
73741
|
+
}
|
|
73742
|
+
function hasNonAsciiHostname(hostname5) {
|
|
73743
|
+
for (const ch of hostname5) {
|
|
73744
|
+
const cp = ch.codePointAt(0);
|
|
73745
|
+
if (cp !== undefined && cp > 127)
|
|
73746
|
+
return true;
|
|
73747
|
+
}
|
|
73748
|
+
return false;
|
|
73749
|
+
}
|
|
73734
73750
|
function isPrivateHost(url3) {
|
|
73735
73751
|
const host = url3.hostname.toLowerCase();
|
|
73736
73752
|
if (host === "localhost" || host === "127.0.0.1" || host === "::1" || host === "0.0.0.0") {
|
|
@@ -73763,13 +73779,182 @@ function validateAndSanitizeUrl(rawUrl) {
|
|
|
73763
73779
|
if (!sanitized.startsWith("https://")) {
|
|
73764
73780
|
return { error: "URL must use HTTPS scheme" };
|
|
73765
73781
|
}
|
|
73782
|
+
try {
|
|
73783
|
+
const url3 = new URL(sanitized);
|
|
73784
|
+
if (hasNonAsciiHostname(url3.hostname)) {
|
|
73785
|
+
return { error: "Non-ASCII hostnames are not allowed" };
|
|
73786
|
+
}
|
|
73787
|
+
if (isPrivateHost(url3)) {
|
|
73788
|
+
return { error: "Private or localhost URLs are not allowed" };
|
|
73789
|
+
}
|
|
73790
|
+
const githubPrPattern = /^https:\/\/github\.com\/([^/]+)\/([^/]+)\/pull\/([0-9]+)\/?$/;
|
|
73791
|
+
if (!githubPrPattern.test(sanitized)) {
|
|
73792
|
+
return {
|
|
73793
|
+
error: "URL must be a GitHub pull request URL (https://github.com/owner/repo/pull/N)"
|
|
73794
|
+
};
|
|
73795
|
+
}
|
|
73796
|
+
return { sanitized };
|
|
73797
|
+
} catch {
|
|
73798
|
+
return { error: "Invalid URL format" };
|
|
73799
|
+
}
|
|
73800
|
+
}
|
|
73801
|
+
function parsePrRef(input, cwd) {
|
|
73802
|
+
const urlMatch = input.match(/^https:\/\/github\.com\/([^/]+)\/([^/]+)\/pull\/(\d+)\/?$/i);
|
|
73803
|
+
if (urlMatch) {
|
|
73804
|
+
return {
|
|
73805
|
+
owner: urlMatch[1],
|
|
73806
|
+
repo: urlMatch[2],
|
|
73807
|
+
number: parseInt(urlMatch[3], 10)
|
|
73808
|
+
};
|
|
73809
|
+
}
|
|
73810
|
+
const shorthandMatch = input.match(/^([^/]+)\/([^#]+)#(\d+)$/);
|
|
73811
|
+
if (shorthandMatch) {
|
|
73812
|
+
return {
|
|
73813
|
+
owner: shorthandMatch[1],
|
|
73814
|
+
repo: shorthandMatch[2],
|
|
73815
|
+
number: parseInt(shorthandMatch[3], 10)
|
|
73816
|
+
};
|
|
73817
|
+
}
|
|
73818
|
+
const bareMatch = input.match(/^(\d+)$/);
|
|
73819
|
+
if (bareMatch) {
|
|
73820
|
+
const prNumber = parseInt(bareMatch[1], 10);
|
|
73821
|
+
const remoteUrl = detectGitRemote(cwd);
|
|
73822
|
+
if (!remoteUrl) {
|
|
73823
|
+
return null;
|
|
73824
|
+
}
|
|
73825
|
+
const parsed = parseGitRemoteUrl(remoteUrl);
|
|
73826
|
+
if (!parsed) {
|
|
73827
|
+
return null;
|
|
73828
|
+
}
|
|
73829
|
+
return {
|
|
73830
|
+
owner: parsed.owner,
|
|
73831
|
+
repo: parsed.repo,
|
|
73832
|
+
number: prNumber
|
|
73833
|
+
};
|
|
73834
|
+
}
|
|
73835
|
+
return null;
|
|
73836
|
+
}
|
|
73837
|
+
function detectGitRemote(cwd) {
|
|
73838
|
+
try {
|
|
73839
|
+
const remoteUrl = _internals35.execSync("git remote get-url origin", {
|
|
73840
|
+
encoding: "utf-8",
|
|
73841
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
73842
|
+
timeout: 5000,
|
|
73843
|
+
...cwd ? { cwd } : {}
|
|
73844
|
+
}).trim();
|
|
73845
|
+
return remoteUrl || null;
|
|
73846
|
+
} catch {
|
|
73847
|
+
return null;
|
|
73848
|
+
}
|
|
73849
|
+
}
|
|
73850
|
+
function parseGitRemoteUrl(remoteUrl) {
|
|
73851
|
+
const httpsMatch = remoteUrl.match(/^https:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?\/?$/i);
|
|
73852
|
+
if (httpsMatch) {
|
|
73853
|
+
return {
|
|
73854
|
+
owner: httpsMatch[1],
|
|
73855
|
+
repo: httpsMatch[2].replace(/\.git$/, "")
|
|
73856
|
+
};
|
|
73857
|
+
}
|
|
73858
|
+
const sshMatch = remoteUrl.match(/^git@github\.com:([^/]+)\/([^/]+?)(?:\.git)?$/i);
|
|
73859
|
+
if (sshMatch) {
|
|
73860
|
+
return {
|
|
73861
|
+
owner: sshMatch[1],
|
|
73862
|
+
repo: sshMatch[2].replace(/\.git$/, "")
|
|
73863
|
+
};
|
|
73864
|
+
}
|
|
73865
|
+
const pathMatch = remoteUrl.match(/\/([^/]+)\/([^/]+?)(?:\.git)?\/?$/);
|
|
73866
|
+
if (pathMatch) {
|
|
73867
|
+
return {
|
|
73868
|
+
owner: pathMatch[1],
|
|
73869
|
+
repo: pathMatch[2].replace(/\.git$/, "")
|
|
73870
|
+
};
|
|
73871
|
+
}
|
|
73872
|
+
return null;
|
|
73873
|
+
}
|
|
73874
|
+
function looksLikePrRef(token) {
|
|
73875
|
+
return /^https?:\/\//i.test(token) || /^[^/]+\/[^#]+#\d+$/.test(token) || /^\d+$/.test(token);
|
|
73876
|
+
}
|
|
73877
|
+
function resolvePrCommandInput(rest, cwd) {
|
|
73878
|
+
if (rest.length === 0) {
|
|
73879
|
+
return null;
|
|
73880
|
+
}
|
|
73881
|
+
const refToken = rest[0];
|
|
73882
|
+
const instructions = sanitizeInstructions(rest.slice(1).join(" "));
|
|
73883
|
+
const isFullUrl = /^https?:\/\//i.test(refToken);
|
|
73884
|
+
const prInfo = parsePrRef(isFullUrl ? sanitizeUrl(refToken) : refToken, cwd);
|
|
73885
|
+
if (!prInfo) {
|
|
73886
|
+
return { error: `Could not parse PR reference from "${refToken}"` };
|
|
73887
|
+
}
|
|
73888
|
+
const prUrl = `https://github.com/${prInfo.owner}/${prInfo.repo}/pull/${prInfo.number}`;
|
|
73889
|
+
const result = validateAndSanitizeUrl(prUrl);
|
|
73890
|
+
if ("error" in result) {
|
|
73891
|
+
return { error: result.error };
|
|
73892
|
+
}
|
|
73893
|
+
return { prUrl: result.sanitized, instructions };
|
|
73894
|
+
}
|
|
73895
|
+
var _internals35, MAX_URL_LEN = 2048, MAX_INSTRUCTIONS_LEN = 1000;
|
|
73896
|
+
var init_pr_ref = __esm(() => {
|
|
73897
|
+
_internals35 = { execSync: execSync2 };
|
|
73898
|
+
});
|
|
73899
|
+
|
|
73900
|
+
// src/commands/issue.ts
|
|
73901
|
+
import { execSync as execSync3 } from "node:child_process";
|
|
73902
|
+
function sanitizeUrl2(raw) {
|
|
73903
|
+
let urlStr = raw.trim();
|
|
73904
|
+
urlStr = urlStr.replace(/\[\s*MODE\s*:[^\]]*\]/gi, "");
|
|
73905
|
+
const fragmentIdx = urlStr.indexOf("#");
|
|
73906
|
+
if (fragmentIdx !== -1) {
|
|
73907
|
+
urlStr = urlStr.slice(0, fragmentIdx);
|
|
73908
|
+
}
|
|
73909
|
+
const queryIdx = urlStr.indexOf("?");
|
|
73910
|
+
if (queryIdx !== -1) {
|
|
73911
|
+
urlStr = urlStr.slice(0, queryIdx);
|
|
73912
|
+
}
|
|
73913
|
+
urlStr = urlStr.replace(/^[A-Za-z][A-Za-z0-9+.-]*:\/\/[^@/]+@/, "https://");
|
|
73914
|
+
if (urlStr.length > MAX_URL_LEN2) {
|
|
73915
|
+
urlStr = urlStr.slice(0, MAX_URL_LEN2);
|
|
73916
|
+
}
|
|
73917
|
+
return urlStr.trim();
|
|
73918
|
+
}
|
|
73919
|
+
function isPrivateHost2(url3) {
|
|
73920
|
+
const host = url3.hostname.toLowerCase();
|
|
73921
|
+
if (host === "localhost" || host === "127.0.0.1" || host === "::1" || host === "0.0.0.0") {
|
|
73922
|
+
return true;
|
|
73923
|
+
}
|
|
73924
|
+
if (host.startsWith("localhost") || host === "localhost.com") {
|
|
73925
|
+
return true;
|
|
73926
|
+
}
|
|
73927
|
+
const ipv4Private = /^10\./;
|
|
73928
|
+
const ipv4172 = /^172\.(1[6-9]|2\d|3[0-1])\./;
|
|
73929
|
+
const ipv4192 = /^192\.168\./;
|
|
73930
|
+
const ipv6Private = /^fe80:/i;
|
|
73931
|
+
const ipv6Unique = /^f[cd][0-9a-f]{2}:/i;
|
|
73932
|
+
if (ipv4Private.test(host) || ipv4172.test(host) || ipv4192.test(host) || ipv6Private.test(host) || ipv6Unique.test(host)) {
|
|
73933
|
+
return true;
|
|
73934
|
+
}
|
|
73935
|
+
if (host.startsWith("::ffff:")) {
|
|
73936
|
+
const inner = host.slice(7);
|
|
73937
|
+
if (ipv4Private.test(inner) || ipv4172.test(inner) || ipv4192.test(inner)) {
|
|
73938
|
+
return true;
|
|
73939
|
+
}
|
|
73940
|
+
}
|
|
73941
|
+
return false;
|
|
73942
|
+
}
|
|
73943
|
+
function validateAndSanitizeUrl2(rawUrl) {
|
|
73944
|
+
const sanitized = sanitizeUrl2(rawUrl);
|
|
73945
|
+
if (!sanitized) {
|
|
73946
|
+
return { error: "Empty URL" };
|
|
73947
|
+
}
|
|
73948
|
+
if (!sanitized.startsWith("https://")) {
|
|
73949
|
+
return { error: "URL must use HTTPS scheme" };
|
|
73950
|
+
}
|
|
73766
73951
|
try {
|
|
73767
73952
|
const url3 = new URL(sanitized);
|
|
73768
73953
|
const hostname5 = url3.hostname;
|
|
73769
73954
|
if (/[\u0080-\u{10FFFF}]/u.test(hostname5)) {
|
|
73770
73955
|
return { error: "Non-ASCII hostnames are not allowed" };
|
|
73771
73956
|
}
|
|
73772
|
-
if (
|
|
73957
|
+
if (isPrivateHost2(url3)) {
|
|
73773
73958
|
return { error: "Private or localhost URLs are not allowed" };
|
|
73774
73959
|
}
|
|
73775
73960
|
const githubIssuePattern = /^https:\/\/github\.com\/([^/]+)\/([^/]+)\/issues\/([0-9]+)\/?$/;
|
|
@@ -73828,7 +74013,7 @@ function parseIssueRef(input) {
|
|
|
73828
74013
|
const bareMatch = input.match(/^(\d+)$/);
|
|
73829
74014
|
if (bareMatch) {
|
|
73830
74015
|
const issueNumber = parseInt(bareMatch[1], 10);
|
|
73831
|
-
const remoteUrl =
|
|
74016
|
+
const remoteUrl = detectGitRemote2();
|
|
73832
74017
|
if (!remoteUrl) {
|
|
73833
74018
|
return null;
|
|
73834
74019
|
}
|
|
@@ -73844,9 +74029,9 @@ function parseIssueRef(input) {
|
|
|
73844
74029
|
}
|
|
73845
74030
|
return null;
|
|
73846
74031
|
}
|
|
73847
|
-
function
|
|
74032
|
+
function detectGitRemote2() {
|
|
73848
74033
|
try {
|
|
73849
|
-
const remoteUrl =
|
|
74034
|
+
const remoteUrl = execSync3("git remote get-url origin", {
|
|
73850
74035
|
encoding: "utf-8",
|
|
73851
74036
|
stdio: ["pipe", "pipe", "pipe"],
|
|
73852
74037
|
timeout: 5000
|
|
@@ -73856,23 +74041,6 @@ function detectGitRemote() {
|
|
|
73856
74041
|
return null;
|
|
73857
74042
|
}
|
|
73858
74043
|
}
|
|
73859
|
-
function parseGitRemoteUrl(remoteUrl) {
|
|
73860
|
-
const httpsMatch = remoteUrl.match(/^https:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?\/?$/i);
|
|
73861
|
-
if (httpsMatch) {
|
|
73862
|
-
return {
|
|
73863
|
-
owner: httpsMatch[1],
|
|
73864
|
-
repo: httpsMatch[2].replace(/\.git$/, "")
|
|
73865
|
-
};
|
|
73866
|
-
}
|
|
73867
|
-
const sshMatch = remoteUrl.match(/^git@github\.com:([^/]+)\/([^/]+?)(?:\.git)?$/i);
|
|
73868
|
-
if (sshMatch) {
|
|
73869
|
-
return {
|
|
73870
|
-
owner: sshMatch[1],
|
|
73871
|
-
repo: sshMatch[2].replace(/\.git$/, "")
|
|
73872
|
-
};
|
|
73873
|
-
}
|
|
73874
|
-
return null;
|
|
73875
|
-
}
|
|
73876
74044
|
function handleIssueCommand(_directory, args2) {
|
|
73877
74045
|
const parsed = parseArgs5(args2);
|
|
73878
74046
|
const rawInput = parsed.rest.join(" ").trim();
|
|
@@ -73880,14 +74048,14 @@ function handleIssueCommand(_directory, args2) {
|
|
|
73880
74048
|
return USAGE5;
|
|
73881
74049
|
}
|
|
73882
74050
|
const isFullUrl = /^https?:\/\//i.test(rawInput);
|
|
73883
|
-
const issueInfo = parseIssueRef(isFullUrl ?
|
|
74051
|
+
const issueInfo = parseIssueRef(isFullUrl ? sanitizeUrl2(rawInput) : rawInput);
|
|
73884
74052
|
if (!issueInfo) {
|
|
73885
74053
|
return `Error: Could not parse issue reference from "${rawInput}"
|
|
73886
74054
|
|
|
73887
74055
|
${USAGE5}`;
|
|
73888
74056
|
}
|
|
73889
74057
|
const issueUrl = `https://github.com/${issueInfo.owner}/${issueInfo.repo}/issues/${issueInfo.number}`;
|
|
73890
|
-
const result =
|
|
74058
|
+
const result = validateAndSanitizeUrl2(issueUrl);
|
|
73891
74059
|
if ("error" in result) {
|
|
73892
74060
|
return `Error: ${result.error}
|
|
73893
74061
|
|
|
@@ -73903,8 +74071,9 @@ ${USAGE5}`;
|
|
|
73903
74071
|
const flagsStr = flags2.length > 0 ? ` ${flags2.join(" ")}` : "";
|
|
73904
74072
|
return `[MODE: ISSUE_INGEST issue="${result.sanitized}"${flagsStr}]`;
|
|
73905
74073
|
}
|
|
73906
|
-
var
|
|
74074
|
+
var MAX_URL_LEN2 = 2048, USAGE5;
|
|
73907
74075
|
var init_issue = __esm(() => {
|
|
74076
|
+
init_pr_ref();
|
|
73908
74077
|
USAGE5 = [
|
|
73909
74078
|
"Usage: /swarm issue <url|owner/repo#N|N> [--plan] [--trace] [--no-repro]",
|
|
73910
74079
|
"",
|
|
@@ -73972,9 +74141,9 @@ async function migrateContextToKnowledge(directory, config3) {
|
|
|
73972
74141
|
skippedReason: "empty-context"
|
|
73973
74142
|
};
|
|
73974
74143
|
}
|
|
73975
|
-
const rawEntries =
|
|
74144
|
+
const rawEntries = _internals36.parseContextMd(contextContent);
|
|
73976
74145
|
if (rawEntries.length === 0) {
|
|
73977
|
-
await
|
|
74146
|
+
await _internals36.writeSentinel(sentinelPath, 0, 0);
|
|
73978
74147
|
return {
|
|
73979
74148
|
migrated: true,
|
|
73980
74149
|
entriesMigrated: 0,
|
|
@@ -73985,10 +74154,10 @@ async function migrateContextToKnowledge(directory, config3) {
|
|
|
73985
74154
|
const existing = await readKnowledge(knowledgePath);
|
|
73986
74155
|
let migrated = 0;
|
|
73987
74156
|
let dropped = 0;
|
|
73988
|
-
const projectName =
|
|
74157
|
+
const projectName = _internals36.inferProjectName(directory);
|
|
73989
74158
|
for (const raw of rawEntries) {
|
|
73990
74159
|
if (config3.validation_enabled !== false) {
|
|
73991
|
-
const category = raw.categoryHint ??
|
|
74160
|
+
const category = raw.categoryHint ?? _internals36.inferCategoryFromText(raw.text);
|
|
73992
74161
|
const result = validateLesson(raw.text, existing.map((e) => e.lesson), {
|
|
73993
74162
|
category,
|
|
73994
74163
|
scope: "global",
|
|
@@ -74008,8 +74177,8 @@ async function migrateContextToKnowledge(directory, config3) {
|
|
|
74008
74177
|
const entry = {
|
|
74009
74178
|
id: randomUUID6(),
|
|
74010
74179
|
tier: "swarm",
|
|
74011
|
-
lesson:
|
|
74012
|
-
category: raw.categoryHint ??
|
|
74180
|
+
lesson: _internals36.truncateLesson(raw.text),
|
|
74181
|
+
category: raw.categoryHint ?? _internals36.inferCategoryFromText(raw.text),
|
|
74013
74182
|
tags: [...inferredTags, `migration:${raw.sourceSection}`],
|
|
74014
74183
|
scope: "global",
|
|
74015
74184
|
confidence: 0.3,
|
|
@@ -74032,7 +74201,7 @@ async function migrateContextToKnowledge(directory, config3) {
|
|
|
74032
74201
|
if (migrated > 0) {
|
|
74033
74202
|
await rewriteKnowledge(knowledgePath, existing);
|
|
74034
74203
|
}
|
|
74035
|
-
await
|
|
74204
|
+
await _internals36.writeSentinel(sentinelPath, migrated, dropped);
|
|
74036
74205
|
log(`[knowledge-migrator] Migrated ${migrated} entries, dropped ${dropped}`);
|
|
74037
74206
|
return {
|
|
74038
74207
|
migrated: true,
|
|
@@ -74042,7 +74211,7 @@ async function migrateContextToKnowledge(directory, config3) {
|
|
|
74042
74211
|
};
|
|
74043
74212
|
}
|
|
74044
74213
|
async function migrateHiveKnowledgeLegacy(config3) {
|
|
74045
|
-
const legacyHivePath =
|
|
74214
|
+
const legacyHivePath = _internals36.resolveLegacyHiveKnowledgePath();
|
|
74046
74215
|
const canonicalHivePath = resolveHiveKnowledgePath();
|
|
74047
74216
|
const sentinelPath = path53.join(path53.dirname(canonicalHivePath), ".hive-knowledge-migrated");
|
|
74048
74217
|
if (existsSync32(sentinelPath)) {
|
|
@@ -74065,7 +74234,7 @@ async function migrateHiveKnowledgeLegacy(config3) {
|
|
|
74065
74234
|
}
|
|
74066
74235
|
const legacyEntries = await readKnowledge(legacyHivePath);
|
|
74067
74236
|
if (legacyEntries.length === 0) {
|
|
74068
|
-
await
|
|
74237
|
+
await _internals36.writeSentinel(sentinelPath, 0, 0);
|
|
74069
74238
|
return {
|
|
74070
74239
|
migrated: true,
|
|
74071
74240
|
entriesMigrated: 0,
|
|
@@ -74113,7 +74282,7 @@ async function migrateHiveKnowledgeLegacy(config3) {
|
|
|
74113
74282
|
const newHiveEntry = {
|
|
74114
74283
|
id: resolvedId,
|
|
74115
74284
|
tier: "hive",
|
|
74116
|
-
lesson:
|
|
74285
|
+
lesson: _internals36.truncateLesson(lesson),
|
|
74117
74286
|
category,
|
|
74118
74287
|
tags: ["migration:legacy-hive"],
|
|
74119
74288
|
scope: scopeTag,
|
|
@@ -74132,7 +74301,7 @@ async function migrateHiveKnowledgeLegacy(config3) {
|
|
|
74132
74301
|
encounter_score: 1
|
|
74133
74302
|
};
|
|
74134
74303
|
try {
|
|
74135
|
-
await
|
|
74304
|
+
await _internals36.appendKnowledge(canonicalHivePath, newHiveEntry);
|
|
74136
74305
|
existingHiveEntries.push(newHiveEntry);
|
|
74137
74306
|
migrated++;
|
|
74138
74307
|
} catch (appendError) {
|
|
@@ -74148,7 +74317,7 @@ async function migrateHiveKnowledgeLegacy(config3) {
|
|
|
74148
74317
|
dropped++;
|
|
74149
74318
|
}
|
|
74150
74319
|
}
|
|
74151
|
-
await
|
|
74320
|
+
await _internals36.writeSentinel(sentinelPath, migrated, dropped);
|
|
74152
74321
|
log(`[knowledge-migrator] Migrated ${migrated} legacy hive entries, dropped ${dropped}`);
|
|
74153
74322
|
return {
|
|
74154
74323
|
migrated: true,
|
|
@@ -74159,7 +74328,7 @@ async function migrateHiveKnowledgeLegacy(config3) {
|
|
|
74159
74328
|
};
|
|
74160
74329
|
}
|
|
74161
74330
|
function parseContextMd(content) {
|
|
74162
|
-
const sections =
|
|
74331
|
+
const sections = _internals36.splitIntoSections(content);
|
|
74163
74332
|
const entries = [];
|
|
74164
74333
|
const seen = new Set;
|
|
74165
74334
|
const sectionPatterns = [
|
|
@@ -74175,7 +74344,7 @@ function parseContextMd(content) {
|
|
|
74175
74344
|
const match = sectionPatterns.find((sp) => sp.pattern.test(section.heading));
|
|
74176
74345
|
if (!match)
|
|
74177
74346
|
continue;
|
|
74178
|
-
const bullets =
|
|
74347
|
+
const bullets = _internals36.extractBullets(section.body);
|
|
74179
74348
|
for (const bullet of bullets) {
|
|
74180
74349
|
if (bullet.length < 15)
|
|
74181
74350
|
continue;
|
|
@@ -74184,9 +74353,9 @@ function parseContextMd(content) {
|
|
|
74184
74353
|
continue;
|
|
74185
74354
|
seen.add(normalized);
|
|
74186
74355
|
entries.push({
|
|
74187
|
-
text:
|
|
74356
|
+
text: _internals36.truncateLesson(bullet),
|
|
74188
74357
|
sourceSection: match.sourceSection,
|
|
74189
|
-
categoryHint:
|
|
74358
|
+
categoryHint: _internals36.inferCategoryFromText(bullet)
|
|
74190
74359
|
});
|
|
74191
74360
|
}
|
|
74192
74361
|
}
|
|
@@ -74292,12 +74461,12 @@ function resolveLegacyHiveKnowledgePath() {
|
|
|
74292
74461
|
}
|
|
74293
74462
|
return path53.join(dataDir, "hive-knowledge.jsonl");
|
|
74294
74463
|
}
|
|
74295
|
-
var
|
|
74464
|
+
var _internals36;
|
|
74296
74465
|
var init_knowledge_migrator = __esm(() => {
|
|
74297
74466
|
init_logger();
|
|
74298
74467
|
init_knowledge_store();
|
|
74299
74468
|
init_knowledge_validator();
|
|
74300
|
-
|
|
74469
|
+
_internals36 = {
|
|
74301
74470
|
appendKnowledge,
|
|
74302
74471
|
migrateContextToKnowledge,
|
|
74303
74472
|
migrateKnowledgeToExternal,
|
|
@@ -78805,9 +78974,9 @@ var init_memory2 = __esm(() => {
|
|
|
78805
78974
|
|
|
78806
78975
|
// src/services/plan-service.ts
|
|
78807
78976
|
async function getPlanData(directory, phaseArg) {
|
|
78808
|
-
const plan = await
|
|
78977
|
+
const plan = await _internals37.loadPlanJsonOnly(directory);
|
|
78809
78978
|
if (plan) {
|
|
78810
|
-
const fullMarkdown =
|
|
78979
|
+
const fullMarkdown = _internals37.derivePlanMarkdown(plan);
|
|
78811
78980
|
if (phaseArg === undefined || phaseArg === null || phaseArg === "") {
|
|
78812
78981
|
return {
|
|
78813
78982
|
hasPlan: true,
|
|
@@ -78850,7 +79019,7 @@ async function getPlanData(directory, phaseArg) {
|
|
|
78850
79019
|
isLegacy: false
|
|
78851
79020
|
};
|
|
78852
79021
|
}
|
|
78853
|
-
const planContent = await
|
|
79022
|
+
const planContent = await _internals37.readSwarmFileAsync(directory, "plan.md");
|
|
78854
79023
|
if (!planContent) {
|
|
78855
79024
|
return {
|
|
78856
79025
|
hasPlan: false,
|
|
@@ -78946,11 +79115,11 @@ async function handlePlanCommand(directory, args2) {
|
|
|
78946
79115
|
const planData = await getPlanData(directory, phaseArg);
|
|
78947
79116
|
return formatPlanMarkdown(planData);
|
|
78948
79117
|
}
|
|
78949
|
-
var
|
|
79118
|
+
var _internals37;
|
|
78950
79119
|
var init_plan_service = __esm(() => {
|
|
78951
79120
|
init_utils2();
|
|
78952
79121
|
init_manager();
|
|
78953
|
-
|
|
79122
|
+
_internals37 = {
|
|
78954
79123
|
loadPlanJsonOnly,
|
|
78955
79124
|
derivePlanMarkdown,
|
|
78956
79125
|
readSwarmFileAsync
|
|
@@ -78962,191 +79131,6 @@ var init_plan = __esm(() => {
|
|
|
78962
79131
|
init_plan_service();
|
|
78963
79132
|
});
|
|
78964
79133
|
|
|
78965
|
-
// src/commands/pr-ref.ts
|
|
78966
|
-
import { execSync as execSync3 } from "node:child_process";
|
|
78967
|
-
function sanitizeUrl2(raw) {
|
|
78968
|
-
let urlStr = raw.trim();
|
|
78969
|
-
urlStr = urlStr.replace(/\[\s*MODE\s*:[^\]]*\]/gi, "");
|
|
78970
|
-
const fragmentIdx = urlStr.indexOf("#");
|
|
78971
|
-
if (fragmentIdx !== -1) {
|
|
78972
|
-
urlStr = urlStr.slice(0, fragmentIdx);
|
|
78973
|
-
}
|
|
78974
|
-
const queryIdx = urlStr.indexOf("?");
|
|
78975
|
-
if (queryIdx !== -1) {
|
|
78976
|
-
urlStr = urlStr.slice(0, queryIdx);
|
|
78977
|
-
}
|
|
78978
|
-
urlStr = urlStr.replace(/^[A-Za-z][A-Za-z0-9+.-]*:\/\/[^@/]+@/, "https://");
|
|
78979
|
-
if (urlStr.length > MAX_URL_LEN2) {
|
|
78980
|
-
urlStr = urlStr.slice(0, MAX_URL_LEN2);
|
|
78981
|
-
}
|
|
78982
|
-
return urlStr.trim();
|
|
78983
|
-
}
|
|
78984
|
-
function sanitizeInstructions(raw) {
|
|
78985
|
-
const collapsed = raw.replace(/\s+/g, " ").trim();
|
|
78986
|
-
const stripped = collapsed.replace(/\[\s*MODE\s*:[^\]]*\]/gi, "");
|
|
78987
|
-
const normalized = stripped.replace(/\s+/g, " ").trim();
|
|
78988
|
-
if (normalized.length <= MAX_INSTRUCTIONS_LEN)
|
|
78989
|
-
return normalized;
|
|
78990
|
-
return `${normalized.slice(0, MAX_INSTRUCTIONS_LEN)}…`;
|
|
78991
|
-
}
|
|
78992
|
-
function hasNonAsciiHostname(hostname5) {
|
|
78993
|
-
for (const ch of hostname5) {
|
|
78994
|
-
const cp = ch.codePointAt(0);
|
|
78995
|
-
if (cp !== undefined && cp > 127)
|
|
78996
|
-
return true;
|
|
78997
|
-
}
|
|
78998
|
-
return false;
|
|
78999
|
-
}
|
|
79000
|
-
function isPrivateHost2(url3) {
|
|
79001
|
-
const host = url3.hostname.toLowerCase();
|
|
79002
|
-
if (host === "localhost" || host === "127.0.0.1" || host === "::1" || host === "0.0.0.0") {
|
|
79003
|
-
return true;
|
|
79004
|
-
}
|
|
79005
|
-
if (host.startsWith("localhost") || host === "localhost.com") {
|
|
79006
|
-
return true;
|
|
79007
|
-
}
|
|
79008
|
-
const ipv4Private = /^10\./;
|
|
79009
|
-
const ipv4172 = /^172\.(1[6-9]|2\d|3[0-1])\./;
|
|
79010
|
-
const ipv4192 = /^192\.168\./;
|
|
79011
|
-
const ipv6Private = /^fe80:/i;
|
|
79012
|
-
const ipv6Unique = /^f[cd][0-9a-f]{2}:/i;
|
|
79013
|
-
if (ipv4Private.test(host) || ipv4172.test(host) || ipv4192.test(host) || ipv6Private.test(host) || ipv6Unique.test(host)) {
|
|
79014
|
-
return true;
|
|
79015
|
-
}
|
|
79016
|
-
if (host.startsWith("::ffff:")) {
|
|
79017
|
-
const inner = host.slice(7);
|
|
79018
|
-
if (ipv4Private.test(inner) || ipv4172.test(inner) || ipv4192.test(inner)) {
|
|
79019
|
-
return true;
|
|
79020
|
-
}
|
|
79021
|
-
}
|
|
79022
|
-
return false;
|
|
79023
|
-
}
|
|
79024
|
-
function validateAndSanitizeUrl2(rawUrl) {
|
|
79025
|
-
const sanitized = sanitizeUrl2(rawUrl);
|
|
79026
|
-
if (!sanitized) {
|
|
79027
|
-
return { error: "Empty URL" };
|
|
79028
|
-
}
|
|
79029
|
-
if (!sanitized.startsWith("https://")) {
|
|
79030
|
-
return { error: "URL must use HTTPS scheme" };
|
|
79031
|
-
}
|
|
79032
|
-
try {
|
|
79033
|
-
const url3 = new URL(sanitized);
|
|
79034
|
-
if (hasNonAsciiHostname(url3.hostname)) {
|
|
79035
|
-
return { error: "Non-ASCII hostnames are not allowed" };
|
|
79036
|
-
}
|
|
79037
|
-
if (isPrivateHost2(url3)) {
|
|
79038
|
-
return { error: "Private or localhost URLs are not allowed" };
|
|
79039
|
-
}
|
|
79040
|
-
const githubPrPattern = /^https:\/\/github\.com\/([^/]+)\/([^/]+)\/pull\/([0-9]+)\/?$/;
|
|
79041
|
-
if (!githubPrPattern.test(sanitized)) {
|
|
79042
|
-
return {
|
|
79043
|
-
error: "URL must be a GitHub pull request URL (https://github.com/owner/repo/pull/N)"
|
|
79044
|
-
};
|
|
79045
|
-
}
|
|
79046
|
-
return { sanitized };
|
|
79047
|
-
} catch {
|
|
79048
|
-
return { error: "Invalid URL format" };
|
|
79049
|
-
}
|
|
79050
|
-
}
|
|
79051
|
-
function parsePrRef(input, cwd) {
|
|
79052
|
-
const urlMatch = input.match(/^https:\/\/github\.com\/([^/]+)\/([^/]+)\/pull\/(\d+)\/?$/i);
|
|
79053
|
-
if (urlMatch) {
|
|
79054
|
-
return {
|
|
79055
|
-
owner: urlMatch[1],
|
|
79056
|
-
repo: urlMatch[2],
|
|
79057
|
-
number: parseInt(urlMatch[3], 10)
|
|
79058
|
-
};
|
|
79059
|
-
}
|
|
79060
|
-
const shorthandMatch = input.match(/^([^/]+)\/([^#]+)#(\d+)$/);
|
|
79061
|
-
if (shorthandMatch) {
|
|
79062
|
-
return {
|
|
79063
|
-
owner: shorthandMatch[1],
|
|
79064
|
-
repo: shorthandMatch[2],
|
|
79065
|
-
number: parseInt(shorthandMatch[3], 10)
|
|
79066
|
-
};
|
|
79067
|
-
}
|
|
79068
|
-
const bareMatch = input.match(/^(\d+)$/);
|
|
79069
|
-
if (bareMatch) {
|
|
79070
|
-
const prNumber = parseInt(bareMatch[1], 10);
|
|
79071
|
-
const remoteUrl = detectGitRemote2(cwd);
|
|
79072
|
-
if (!remoteUrl) {
|
|
79073
|
-
return null;
|
|
79074
|
-
}
|
|
79075
|
-
const parsed = parseGitRemoteUrl2(remoteUrl);
|
|
79076
|
-
if (!parsed) {
|
|
79077
|
-
return null;
|
|
79078
|
-
}
|
|
79079
|
-
return {
|
|
79080
|
-
owner: parsed.owner,
|
|
79081
|
-
repo: parsed.repo,
|
|
79082
|
-
number: prNumber
|
|
79083
|
-
};
|
|
79084
|
-
}
|
|
79085
|
-
return null;
|
|
79086
|
-
}
|
|
79087
|
-
function detectGitRemote2(cwd) {
|
|
79088
|
-
try {
|
|
79089
|
-
const remoteUrl = _internals37.execSync("git remote get-url origin", {
|
|
79090
|
-
encoding: "utf-8",
|
|
79091
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
79092
|
-
timeout: 5000,
|
|
79093
|
-
...cwd ? { cwd } : {}
|
|
79094
|
-
}).trim();
|
|
79095
|
-
return remoteUrl || null;
|
|
79096
|
-
} catch {
|
|
79097
|
-
return null;
|
|
79098
|
-
}
|
|
79099
|
-
}
|
|
79100
|
-
function parseGitRemoteUrl2(remoteUrl) {
|
|
79101
|
-
const httpsMatch = remoteUrl.match(/^https:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?\/?$/i);
|
|
79102
|
-
if (httpsMatch) {
|
|
79103
|
-
return {
|
|
79104
|
-
owner: httpsMatch[1],
|
|
79105
|
-
repo: httpsMatch[2].replace(/\.git$/, "")
|
|
79106
|
-
};
|
|
79107
|
-
}
|
|
79108
|
-
const sshMatch = remoteUrl.match(/^git@github\.com:([^/]+)\/([^/]+?)(?:\.git)?$/i);
|
|
79109
|
-
if (sshMatch) {
|
|
79110
|
-
return {
|
|
79111
|
-
owner: sshMatch[1],
|
|
79112
|
-
repo: sshMatch[2].replace(/\.git$/, "")
|
|
79113
|
-
};
|
|
79114
|
-
}
|
|
79115
|
-
const pathMatch = remoteUrl.match(/\/([^/]+)\/([^/]+?)(?:\.git)?\/?$/);
|
|
79116
|
-
if (pathMatch) {
|
|
79117
|
-
return {
|
|
79118
|
-
owner: pathMatch[1],
|
|
79119
|
-
repo: pathMatch[2].replace(/\.git$/, "")
|
|
79120
|
-
};
|
|
79121
|
-
}
|
|
79122
|
-
return null;
|
|
79123
|
-
}
|
|
79124
|
-
function looksLikePrRef(token) {
|
|
79125
|
-
return /^https?:\/\//i.test(token) || /^[^/]+\/[^#]+#\d+$/.test(token) || /^\d+$/.test(token);
|
|
79126
|
-
}
|
|
79127
|
-
function resolvePrCommandInput(rest, cwd) {
|
|
79128
|
-
if (rest.length === 0) {
|
|
79129
|
-
return null;
|
|
79130
|
-
}
|
|
79131
|
-
const refToken = rest[0];
|
|
79132
|
-
const instructions = sanitizeInstructions(rest.slice(1).join(" "));
|
|
79133
|
-
const isFullUrl = /^https?:\/\//i.test(refToken);
|
|
79134
|
-
const prInfo = parsePrRef(isFullUrl ? sanitizeUrl2(refToken) : refToken, cwd);
|
|
79135
|
-
if (!prInfo) {
|
|
79136
|
-
return { error: `Could not parse PR reference from "${refToken}"` };
|
|
79137
|
-
}
|
|
79138
|
-
const prUrl = `https://github.com/${prInfo.owner}/${prInfo.repo}/pull/${prInfo.number}`;
|
|
79139
|
-
const result = validateAndSanitizeUrl2(prUrl);
|
|
79140
|
-
if ("error" in result) {
|
|
79141
|
-
return { error: result.error };
|
|
79142
|
-
}
|
|
79143
|
-
return { prUrl: result.sanitized, instructions };
|
|
79144
|
-
}
|
|
79145
|
-
var _internals37, MAX_URL_LEN2 = 2048, MAX_INSTRUCTIONS_LEN = 1000;
|
|
79146
|
-
var init_pr_ref = __esm(() => {
|
|
79147
|
-
_internals37 = { execSync: execSync3 };
|
|
79148
|
-
});
|
|
79149
|
-
|
|
79150
79134
|
// src/commands/pr-feedback.ts
|
|
79151
79135
|
function handlePrFeedbackCommand(directory, args2) {
|
|
79152
79136
|
const rest = args2.filter((t) => t.trim().length > 0);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-swarm",
|
|
3
|
-
"version": "7.68.
|
|
3
|
+
"version": "7.68.2",
|
|
4
4
|
"description": "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|