harnessed 3.5.0 → 3.6.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/THIRD-PARTY-NOTICES.md +34 -0
- package/dist/cli.mjs +224 -27
- package/dist/cli.mjs.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -2
- package/workflows/disciplines/operational.yaml +18 -0
- package/workflows/judgments/user-overrides.yaml +82 -0
- package/workflows/role-prompts.yaml +47 -2
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Third-Party Notices
|
|
2
|
+
|
|
3
|
+
`harnessed` itself is licensed under the Apache License 2.0 (see `LICENSE` and
|
|
4
|
+
`NOTICE` at the repo root). This file enumerates third-party material whose
|
|
5
|
+
text — verbatim or paraphrased — is incorporated into shipped harnessed source
|
|
6
|
+
files. Upstream attributions for each capability *manifest* (and its installed
|
|
7
|
+
side effects on the user's machine) continue to be tracked separately in
|
|
8
|
+
`NOTICES.md` (auto-populated by `harnessed install` from each manifest's
|
|
9
|
+
`metadata.upstream.notice` field).
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## mattpocock/skills
|
|
14
|
+
|
|
15
|
+
- **Source**: https://github.com/mattpocock/skills
|
|
16
|
+
- **Commit SHA pinned**: `b8be62ffacb0118fa3eaa29a0923c87c8c11985c`
|
|
17
|
+
- **License**: MIT
|
|
18
|
+
- **Used in**: `workflows/role-prompts.yaml` — paraphrased methodology
|
|
19
|
+
excerpts inlined into the `task-clarify`, `task-code`, and `discuss-subtask`
|
|
20
|
+
sub-workflow role-prompt entries (introduced in v3.6.0 Phase 1). Each
|
|
21
|
+
paraphrased block carries an inline attribution comment naming the source
|
|
22
|
+
SKILL.md path and the pinned commit SHA.
|
|
23
|
+
- **Excerpts paraphrased (not redistributed verbatim)**:
|
|
24
|
+
- `skills/engineering/grill-with-docs/SKILL.md` → `task-clarify.responsibility` + checklist
|
|
25
|
+
- `skills/engineering/zoom-out/SKILL.md` → `task-code.checklist`
|
|
26
|
+
- `skills/engineering/improve-codebase-architecture/SKILL.md` → `task-code.checklist`
|
|
27
|
+
- `skills/productivity/grill-me/SKILL.md` → `discuss-subtask.responsibility` + checklist
|
|
28
|
+
- **Full upstream license text**: preserved at
|
|
29
|
+
`.planning/v3.6.0/mattpocock-source/LICENSE` (not shipped in the npm
|
|
30
|
+
tarball; retained for audit + license-compliance evidence). The SHA
|
|
31
|
+
metadata + re-fetch instructions live in
|
|
32
|
+
`.planning/v3.6.0/mattpocock-source/SHA.txt`.
|
|
33
|
+
- **Scope of redistribution**: only the paraphrased excerpts above are
|
|
34
|
+
redistributed (no verbatim SKILL.md content ships in the npm tarball).
|
package/dist/cli.mjs
CHANGED
|
@@ -621,6 +621,105 @@ var init_check_planning_with_files = __esm({
|
|
|
621
621
|
REMEDIATION = "install via Claude Code plugin marketplace: `claude plugin install planning-with-files` (requires >=2.2.0 per R20.15 + D-15)";
|
|
622
622
|
}
|
|
623
623
|
});
|
|
624
|
+
|
|
625
|
+
// src/cli/lib/check-mattpocock-skills.ts
|
|
626
|
+
var check_mattpocock_skills_exports = {};
|
|
627
|
+
__export(check_mattpocock_skills_exports, {
|
|
628
|
+
checkMattpocockSkills: () => checkMattpocockSkills
|
|
629
|
+
});
|
|
630
|
+
async function checkMattpocockSkills() {
|
|
631
|
+
const pluginRoot = join(
|
|
632
|
+
homedir(),
|
|
633
|
+
".claude",
|
|
634
|
+
"plugins",
|
|
635
|
+
"cache",
|
|
636
|
+
"mattpocock-skills",
|
|
637
|
+
"mattpocock-skills"
|
|
638
|
+
);
|
|
639
|
+
const skillRoot = join(homedir(), ".claude", "skills", "mattpocock-skills");
|
|
640
|
+
try {
|
|
641
|
+
const entries = await readdir(pluginRoot);
|
|
642
|
+
const versions = entries.filter((e) => /^\d+\.\d+/.test(e));
|
|
643
|
+
if (versions.length > 0) {
|
|
644
|
+
return {
|
|
645
|
+
name: "mattpocock-skills",
|
|
646
|
+
status: "pass",
|
|
647
|
+
message: `installed as plugin (version ${versions.join(", ")})`
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
} catch {
|
|
651
|
+
}
|
|
652
|
+
try {
|
|
653
|
+
await stat(skillRoot);
|
|
654
|
+
return {
|
|
655
|
+
name: "mattpocock-skills",
|
|
656
|
+
status: "pass",
|
|
657
|
+
message: `installed as user-skill (${skillRoot})`
|
|
658
|
+
};
|
|
659
|
+
} catch {
|
|
660
|
+
}
|
|
661
|
+
return {
|
|
662
|
+
name: "mattpocock-skills",
|
|
663
|
+
status: "warn",
|
|
664
|
+
message: "not installed (plugin cache + user-skill paths both missing)",
|
|
665
|
+
fix: REMEDIATION2
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
var REMEDIATION2;
|
|
669
|
+
var init_check_mattpocock_skills = __esm({
|
|
670
|
+
"src/cli/lib/check-mattpocock-skills.ts"() {
|
|
671
|
+
REMEDIATION2 = "install via Claude Code plugin marketplace: `claude plugin install mattpocock-skills` (or git clone https://github.com/mattpocock/skills ~/.claude/skills/mattpocock-skills); methodology fallback already inline in role-prompts.yaml per v3.6.0 Phase 1 \u2014 install is optional but enables /grill-with-docs /zoom-out etc. SlashCommand acceleration";
|
|
672
|
+
}
|
|
673
|
+
});
|
|
674
|
+
|
|
675
|
+
// src/cli/lib/check-mcp-availability.ts
|
|
676
|
+
var check_mcp_availability_exports = {};
|
|
677
|
+
__export(check_mcp_availability_exports, {
|
|
678
|
+
checkMcpAvailability: () => checkMcpAvailability
|
|
679
|
+
});
|
|
680
|
+
async function checkMcpAvailability() {
|
|
681
|
+
const settingsPath3 = join(homedir(), ".claude", "settings.json");
|
|
682
|
+
let installed = [];
|
|
683
|
+
let missing = [...TARGET_SERVERS];
|
|
684
|
+
try {
|
|
685
|
+
const raw = await readFile(settingsPath3, "utf8");
|
|
686
|
+
const parsed = JSON.parse(raw);
|
|
687
|
+
const servers = parsed.mcpServers ?? {};
|
|
688
|
+
const serverNames = Object.keys(servers);
|
|
689
|
+
installed = TARGET_SERVERS.filter(
|
|
690
|
+
(s) => serverNames.some((n) => n.includes(s) || s.includes(n))
|
|
691
|
+
);
|
|
692
|
+
missing = TARGET_SERVERS.filter((s) => !installed.includes(s));
|
|
693
|
+
} catch {
|
|
694
|
+
}
|
|
695
|
+
if (missing.length === 0) {
|
|
696
|
+
return {
|
|
697
|
+
name: "MCP servers (tavily/exa/chrome-devtools)",
|
|
698
|
+
status: "pass",
|
|
699
|
+
message: `all 3 installed: ${installed.join(", ")}`
|
|
700
|
+
};
|
|
701
|
+
}
|
|
702
|
+
if (installed.length === 0) {
|
|
703
|
+
return {
|
|
704
|
+
name: "MCP servers (tavily/exa/chrome-devtools)",
|
|
705
|
+
status: "warn",
|
|
706
|
+
message: "none of 3 target MCP servers installed in ~/.claude/settings.json",
|
|
707
|
+
fix: "install via `claude mcp add <server-name>`; harnessed routes web-search to tavily/exa per workflows/judgments/web-search-routing.yaml \u2014 without them, falls back to WebFetch/WebSearch built-in (degraded but functional)"
|
|
708
|
+
};
|
|
709
|
+
}
|
|
710
|
+
return {
|
|
711
|
+
name: "MCP servers (tavily/exa/chrome-devtools)",
|
|
712
|
+
status: "warn",
|
|
713
|
+
message: `${installed.length}/3 installed: ${installed.join(", ")}; missing: ${missing.join(", ")}`,
|
|
714
|
+
fix: `install missing via \`claude mcp add ${missing.join(" && claude mcp add ")}\``
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
var TARGET_SERVERS;
|
|
718
|
+
var init_check_mcp_availability = __esm({
|
|
719
|
+
"src/cli/lib/check-mcp-availability.ts"() {
|
|
720
|
+
TARGET_SERVERS = ["tavily-mcp", "exa-mcp", "chrome-devtools"];
|
|
721
|
+
}
|
|
722
|
+
});
|
|
624
723
|
function statePath() {
|
|
625
724
|
return harnessedFile("current-workflow.json");
|
|
626
725
|
}
|
|
@@ -853,7 +952,7 @@ var init_resume = __esm({
|
|
|
853
952
|
|
|
854
953
|
// package.json
|
|
855
954
|
var package_default = {
|
|
856
|
-
version: "3.
|
|
955
|
+
version: "3.6.0"};
|
|
857
956
|
|
|
858
957
|
// src/manifest/errors.ts
|
|
859
958
|
function instancePathToKeyPath(instancePath) {
|
|
@@ -1704,7 +1803,7 @@ function renderHumanTable(records) {
|
|
|
1704
1803
|
}
|
|
1705
1804
|
}
|
|
1706
1805
|
function pipeToJq(filterExpr, lines) {
|
|
1707
|
-
return new Promise((
|
|
1806
|
+
return new Promise((resolve15, reject) => {
|
|
1708
1807
|
const child = spawn("jq", [filterExpr], {
|
|
1709
1808
|
stdio: ["pipe", "inherit", "inherit"],
|
|
1710
1809
|
windowsHide: true
|
|
@@ -1713,12 +1812,12 @@ function pipeToJq(filterExpr, lines) {
|
|
|
1713
1812
|
const e = err2;
|
|
1714
1813
|
if (e.code === "ENOENT") {
|
|
1715
1814
|
console.error(t("audit_log.jq_missing"));
|
|
1716
|
-
|
|
1815
|
+
resolve15(1);
|
|
1717
1816
|
} else {
|
|
1718
1817
|
reject(err2);
|
|
1719
1818
|
}
|
|
1720
1819
|
});
|
|
1721
|
-
child.on("close", (code) =>
|
|
1820
|
+
child.on("close", (code) => resolve15(code ?? 0));
|
|
1722
1821
|
child.stdin.write(lines.join("\n"));
|
|
1723
1822
|
child.stdin.end();
|
|
1724
1823
|
});
|
|
@@ -2026,18 +2125,26 @@ async function checkAgentTeamsEnv() {
|
|
|
2026
2125
|
async function checkPlanningPlugin() {
|
|
2027
2126
|
return (await Promise.resolve().then(() => (init_check_planning_with_files(), check_planning_with_files_exports))).checkPlanningWithFiles();
|
|
2028
2127
|
}
|
|
2128
|
+
async function checkMattpocockSkillsInstall() {
|
|
2129
|
+
return (await Promise.resolve().then(() => (init_check_mattpocock_skills(), check_mattpocock_skills_exports))).checkMattpocockSkills();
|
|
2130
|
+
}
|
|
2131
|
+
async function checkMcpAvailabilityCheck() {
|
|
2132
|
+
return (await Promise.resolve().then(() => (init_check_mcp_availability(), check_mcp_availability_exports))).checkMcpAvailability();
|
|
2133
|
+
}
|
|
2029
2134
|
function registerDoctor(program2) {
|
|
2030
2135
|
program2.command("doctor").description(
|
|
2031
|
-
"Preflight checks (Node / MCP scope / jq / Win bash / origin URL / gstack prefix / deprecations / token budget / Agent Teams / planning-with-files)"
|
|
2136
|
+
"Preflight checks (Node / MCP scope / jq / Win bash / origin URL / gstack prefix / deprecations / token budget / Agent Teams / planning-with-files / mattpocock-skills / MCP availability)"
|
|
2032
2137
|
).option("--json", "output JSON instead of human-readable").action(async (opts) => {
|
|
2033
|
-
const [mcp, origin, gstack, dep, tok, at, ppwf] = await Promise.all([
|
|
2138
|
+
const [mcp, origin, gstack, dep, tok, at, ppwf, matt, mcpAvail] = await Promise.all([
|
|
2034
2139
|
checkMcpScope(),
|
|
2035
2140
|
checkOriginUrl(),
|
|
2036
2141
|
checkGstackPrefix(),
|
|
2037
2142
|
checkDeprecations2(),
|
|
2038
2143
|
checkTokenBudget2(),
|
|
2039
2144
|
checkAgentTeamsEnv(),
|
|
2040
|
-
checkPlanningPlugin()
|
|
2145
|
+
checkPlanningPlugin(),
|
|
2146
|
+
checkMattpocockSkillsInstall(),
|
|
2147
|
+
checkMcpAvailabilityCheck()
|
|
2041
2148
|
]);
|
|
2042
2149
|
const results = [
|
|
2043
2150
|
checkNodeVersion(),
|
|
@@ -2049,7 +2156,9 @@ function registerDoctor(program2) {
|
|
|
2049
2156
|
dep,
|
|
2050
2157
|
tok,
|
|
2051
2158
|
at,
|
|
2052
|
-
ppwf
|
|
2159
|
+
ppwf,
|
|
2160
|
+
matt,
|
|
2161
|
+
mcpAvail
|
|
2053
2162
|
];
|
|
2054
2163
|
const hasFail = results.some((r) => r.status === "fail");
|
|
2055
2164
|
const hasWarn = results.some((r) => r.status === "warn");
|
|
@@ -2530,6 +2639,22 @@ var JudgmentRulesFile = Type.Object(
|
|
|
2530
2639
|
{ additionalProperties: false }
|
|
2531
2640
|
);
|
|
2532
2641
|
Type.Union([JudgmentTriggersFile, JudgmentRulesFile]);
|
|
2642
|
+
var UserOverrideEntry = Type.Object(
|
|
2643
|
+
{
|
|
2644
|
+
id: Type.String({ minLength: 1 }),
|
|
2645
|
+
// kebab-case (e.g. 'brainstorm', 'arch-review')
|
|
2646
|
+
keywords: Type.Array(Type.String({ minLength: 1 }), { minItems: 1 }),
|
|
2647
|
+
triggers: Type.Array(Type.String({ minLength: 1 }), { minItems: 1 })
|
|
2648
|
+
},
|
|
2649
|
+
{ additionalProperties: false }
|
|
2650
|
+
);
|
|
2651
|
+
var UserOverridesFile = Type.Object(
|
|
2652
|
+
{
|
|
2653
|
+
schema_version: Type.Literal("harnessed.user-overrides.v1"),
|
|
2654
|
+
overrides: Type.Array(UserOverrideEntry, { minItems: 1 })
|
|
2655
|
+
},
|
|
2656
|
+
{ additionalProperties: false }
|
|
2657
|
+
);
|
|
2533
2658
|
|
|
2534
2659
|
// src/workflow/judgmentResolver.ts
|
|
2535
2660
|
var TriggerNotFoundError = class extends Error {
|
|
@@ -2544,6 +2669,10 @@ var TriggerNotFoundError = class extends Error {
|
|
|
2544
2669
|
};
|
|
2545
2670
|
var _fileCache = /* @__PURE__ */ new Map();
|
|
2546
2671
|
async function resolveJudgmentGate(gateRef, context, packageRoot) {
|
|
2672
|
+
const userOverrides = context.user_overrides;
|
|
2673
|
+
if (Array.isArray(userOverrides) && userOverrides.includes(gateRef)) {
|
|
2674
|
+
return true;
|
|
2675
|
+
}
|
|
2547
2676
|
const parts = gateRef.split(".");
|
|
2548
2677
|
if (parts.length !== 4 || parts[0] !== "judgments") {
|
|
2549
2678
|
throw new Error(`Invalid gate ref: ${gateRef}`);
|
|
@@ -3193,13 +3322,33 @@ Five triggers (any one suffices):
|
|
|
3193
3322
|
5. **fullstack_three_way** \u2014 the task is a synchronized fullstack push (frontend + backend + tests) requiring API contract alignment across three roles simultaneously.
|
|
3194
3323
|
|
|
3195
3324
|
If none of the five apply, omit \`needs_teams_escalation\` (defaults to false) and proceed normally.`;
|
|
3325
|
+
var TRANSPARENT_SKIP_RULES = `When you encounter a phase gate or routing decision where the input context is missing key fields, default-valued, or contradictory, do NOT proceed silently. Instead, skip the phase and emit a one-line transparent explanation:
|
|
3326
|
+
|
|
3327
|
+
"Skipped <phase>, because <reason>. Tell me if you actually need it."
|
|
3328
|
+
(\u4E2D\u6587: "\u8FD9\u6B21\u8DF3\u8FC7\u4E86 <phase>, \u56E0\u4E3A <reason>. \u5982\u679C\u4F60\u8BA4\u4E3A\u9700\u8981\u8BF7\u660E\u8BF4.")
|
|
3329
|
+
|
|
3330
|
+
This applies to: strategic-layer review skip / phase-layer clarification skip / subtask-brainstorming skip / TDD enforcement skip / Agent Teams escalation skip. Chain-isolation rule: skipping one layer does NOT mandate skipping subsequent layers \u2014 each layer is independently evaluated.`;
|
|
3331
|
+
var AGENT_TEAMS_PREVENTION_RULES = `If you signal needs_teams_escalation=true, ALSO advise the user on these 4 Agent Teams prevention rules in your escalation_reason or summary (the user will be the one calling TeamCreate / SendMessage / TeamDelete; remind them upfront):
|
|
3332
|
+
|
|
3333
|
+
1. **Session-scoped**: Teams live only in the current Claude Code session. \`/resume\` loses all teammates. Do not treat teams as persistent state \u2014 finish team work within one session.
|
|
3334
|
+
|
|
3335
|
+
2. **Cleanup mandatory**: Before session ends, send \`SendMessage(to=<teammate>, content="shutdown_request")\` to each teammate, then call \`TeamDelete\`. Orphan teammates consume resources. This is a hard rule, not advisory.
|
|
3336
|
+
|
|
3337
|
+
3. **Token cost estimation**: Before creating a team, estimate \`team_cost \u2248 N_teammates \xD7 N_rounds \xD7 avg_tokens_per_round + N_teammates \xD7 initial_brief_tokens\`. Compare to subagent fan-out cost (\`\u2248 N_subagents \xD7 (initial_brief + summary_tokens)\`). Only open a team when \`team_cost < 2 \xD7 subagent_cost\` \u2014 otherwise prefer fan-out.
|
|
3338
|
+
|
|
3339
|
+
4. **Brief must be self-contained**: Each teammate launches WITHOUT main-session context. The Agent() prompt must include enough background, file paths, success criteria, and counter-positions so the teammate can work independently. Generic prompts produce shallow output.`;
|
|
3340
|
+
var CRITICAL_SYSTEM_REMINDER = `${ESCALATION_RULES}
|
|
3341
|
+
|
|
3342
|
+
${TRANSPARENT_SKIP_RULES}
|
|
3343
|
+
|
|
3344
|
+
${AGENT_TEAMS_PREVENTION_RULES}`;
|
|
3196
3345
|
function buildAgentDef(skillName, rolePrompts, workflowName, modelTierOverride) {
|
|
3197
3346
|
const rp = rolePrompts?.[skillName] ?? (workflowName ? rolePrompts?.[workflowName] : void 0);
|
|
3198
3347
|
if (!rp) {
|
|
3199
3348
|
return {
|
|
3200
3349
|
description: `harnessed workflow phase: ${skillName}`,
|
|
3201
3350
|
prompt: `You are executing the '${skillName}' workflow phase. Follow the phase intent and emit a structured COMPLETE signal when done.`,
|
|
3202
|
-
criticalSystemReminder_EXPERIMENTAL:
|
|
3351
|
+
criticalSystemReminder_EXPERIMENTAL: CRITICAL_SYSTEM_REMINDER,
|
|
3203
3352
|
...modelTierOverride ? { model: modelTierOverride } : {}
|
|
3204
3353
|
};
|
|
3205
3354
|
}
|
|
@@ -3220,7 +3369,7 @@ ${rp.checklist.map((c, i) => ` ${i + 1}. ${c}`).join("\n")}` : "";
|
|
|
3220
3369
|
return {
|
|
3221
3370
|
description: rp.description,
|
|
3222
3371
|
prompt,
|
|
3223
|
-
criticalSystemReminder_EXPERIMENTAL:
|
|
3372
|
+
criticalSystemReminder_EXPERIMENTAL: CRITICAL_SYSTEM_REMINDER,
|
|
3224
3373
|
...modelTierOverride ? { model: modelTierOverride } : {}
|
|
3225
3374
|
};
|
|
3226
3375
|
}
|
|
@@ -3436,6 +3585,46 @@ function getPackageRoot() {
|
|
|
3436
3585
|
}
|
|
3437
3586
|
return resolve(thisDir, "..", "..", "..");
|
|
3438
3587
|
}
|
|
3588
|
+
async function loadUserOverrides(packageRoot) {
|
|
3589
|
+
const yamlPath = resolve(packageRoot, "workflows", "judgments", "user-overrides.yaml");
|
|
3590
|
+
let raw;
|
|
3591
|
+
try {
|
|
3592
|
+
raw = await readFile(yamlPath, "utf8");
|
|
3593
|
+
} catch {
|
|
3594
|
+
return [];
|
|
3595
|
+
}
|
|
3596
|
+
let parsed;
|
|
3597
|
+
try {
|
|
3598
|
+
parsed = parse(raw);
|
|
3599
|
+
} catch {
|
|
3600
|
+
return [];
|
|
3601
|
+
}
|
|
3602
|
+
if (!Value.Check(UserOverridesFile, parsed)) {
|
|
3603
|
+
return [];
|
|
3604
|
+
}
|
|
3605
|
+
const valid = parsed;
|
|
3606
|
+
return valid.overrides.map((o) => ({
|
|
3607
|
+
id: o.id,
|
|
3608
|
+
keywords: [...o.keywords],
|
|
3609
|
+
triggers: [...o.triggers]
|
|
3610
|
+
}));
|
|
3611
|
+
}
|
|
3612
|
+
function extractMatchedTriggers(userText, overrides) {
|
|
3613
|
+
if (!userText || overrides.length === 0) return [];
|
|
3614
|
+
const haystack = userText.toLowerCase();
|
|
3615
|
+
const matched = /* @__PURE__ */ new Set();
|
|
3616
|
+
for (const entry of overrides) {
|
|
3617
|
+
for (const kw of entry.keywords) {
|
|
3618
|
+
if (haystack.includes(kw.toLowerCase())) {
|
|
3619
|
+
for (const trigger of entry.triggers) matched.add(trigger);
|
|
3620
|
+
break;
|
|
3621
|
+
}
|
|
3622
|
+
}
|
|
3623
|
+
}
|
|
3624
|
+
return [...matched];
|
|
3625
|
+
}
|
|
3626
|
+
|
|
3627
|
+
// src/cli/run.ts
|
|
3439
3628
|
var PACKAGE_ROOT = getPackageRoot();
|
|
3440
3629
|
var WORKFLOWS_DIR = join(PACKAGE_ROOT, "workflows");
|
|
3441
3630
|
var _autoChainCache = null;
|
|
@@ -3473,11 +3662,19 @@ function registerRun(program2) {
|
|
|
3473
3662
|
);
|
|
3474
3663
|
process.exit(2);
|
|
3475
3664
|
}
|
|
3665
|
+
const overrides = await loadUserOverrides(PACKAGE_ROOT);
|
|
3666
|
+
const matchedTriggers = extractMatchedTriggers(task, overrides);
|
|
3667
|
+
if (matchedTriggers.length > 0) {
|
|
3668
|
+
console.error(
|
|
3669
|
+
`\u2139 user-override detected: ${matchedTriggers.length} trigger(s) forced fires=true via keyword match (${matchedTriggers.join(", ")})`
|
|
3670
|
+
);
|
|
3671
|
+
}
|
|
3476
3672
|
const gateContext = {
|
|
3477
3673
|
task,
|
|
3478
3674
|
...raw.model ? { modelOverride: raw.model } : {},
|
|
3479
3675
|
...raw.maxIterations ? { maxIterations: raw.maxIterations } : {},
|
|
3480
|
-
...raw.staged ? { staged: true } : {}
|
|
3676
|
+
...raw.staged ? { staged: true } : {},
|
|
3677
|
+
...matchedTriggers.length > 0 ? { user_overrides: matchedTriggers } : {}
|
|
3481
3678
|
};
|
|
3482
3679
|
if (raw.dryRun) {
|
|
3483
3680
|
console.log(JSON.stringify({ workflow: name, yamlPath, gateContext }, null, 2));
|
|
@@ -4052,7 +4249,7 @@ async function isPluginRegistered(pluginName) {
|
|
|
4052
4249
|
return Object.keys(plugins).some((k) => k.split("@")[0] === pluginName);
|
|
4053
4250
|
}
|
|
4054
4251
|
function runArgs(claudeArgs, cwd, timeoutMs = 15e3) {
|
|
4055
|
-
return new Promise((
|
|
4252
|
+
return new Promise((resolve15) => {
|
|
4056
4253
|
const isWin = process.platform === "win32";
|
|
4057
4254
|
const child = isWin ? spawn("cmd.exe", ["/c", "claude", ...claudeArgs], { cwd, windowsHide: true }) : spawn("claude", claudeArgs, { cwd, shell: false });
|
|
4058
4255
|
let stderr = "";
|
|
@@ -4061,15 +4258,15 @@ function runArgs(claudeArgs, cwd, timeoutMs = 15e3) {
|
|
|
4061
4258
|
});
|
|
4062
4259
|
const timer = setTimeout(() => {
|
|
4063
4260
|
child.kill("SIGKILL");
|
|
4064
|
-
|
|
4261
|
+
resolve15({ exitCode: -1, stderr: `${stderr}[timeout after ${timeoutMs}ms]` });
|
|
4065
4262
|
}, timeoutMs);
|
|
4066
4263
|
child.on("error", (e) => {
|
|
4067
4264
|
clearTimeout(timer);
|
|
4068
|
-
|
|
4265
|
+
resolve15({ exitCode: -1, stderr: `${stderr}${e.message}` });
|
|
4069
4266
|
});
|
|
4070
4267
|
child.on("close", (code) => {
|
|
4071
4268
|
clearTimeout(timer);
|
|
4072
|
-
|
|
4269
|
+
resolve15({ exitCode: code ?? -1, stderr });
|
|
4073
4270
|
});
|
|
4074
4271
|
});
|
|
4075
4272
|
}
|
|
@@ -4248,10 +4445,10 @@ async function spawnCmd(ctx, cmd, args, timeoutMs) {
|
|
|
4248
4445
|
child.stderr?.setEncoding("utf8").on("data", (chunk) => {
|
|
4249
4446
|
stderr += chunk;
|
|
4250
4447
|
});
|
|
4251
|
-
return await new Promise((
|
|
4448
|
+
return await new Promise((resolve15) => {
|
|
4252
4449
|
const timer = setTimeout(() => {
|
|
4253
4450
|
child.kill("SIGKILL");
|
|
4254
|
-
|
|
4451
|
+
resolve15({
|
|
4255
4452
|
ok: false,
|
|
4256
4453
|
phase: "spawn",
|
|
4257
4454
|
error: {
|
|
@@ -4266,7 +4463,7 @@ async function spawnCmd(ctx, cmd, args, timeoutMs) {
|
|
|
4266
4463
|
}, effectiveTimeoutMs);
|
|
4267
4464
|
child.on("error", (err2) => {
|
|
4268
4465
|
clearTimeout(timer);
|
|
4269
|
-
|
|
4466
|
+
resolve15({
|
|
4270
4467
|
ok: false,
|
|
4271
4468
|
phase: "spawn",
|
|
4272
4469
|
error: {
|
|
@@ -4281,14 +4478,14 @@ async function spawnCmd(ctx, cmd, args, timeoutMs) {
|
|
|
4281
4478
|
});
|
|
4282
4479
|
child.on("close", (code) => {
|
|
4283
4480
|
clearTimeout(timer);
|
|
4284
|
-
|
|
4481
|
+
resolve15({ ok: true, exitCode: code ?? -1, stdout: stdout2, stderr });
|
|
4285
4482
|
});
|
|
4286
4483
|
});
|
|
4287
4484
|
}
|
|
4288
4485
|
|
|
4289
4486
|
// src/installers/gitCloneWithSetup.ts
|
|
4290
4487
|
function gitRevParseHead(cwd, timeoutMs = 1e4) {
|
|
4291
|
-
return new Promise((
|
|
4488
|
+
return new Promise((resolve15) => {
|
|
4292
4489
|
const isWin = process.platform === "win32";
|
|
4293
4490
|
const child = isWin ? spawn("cmd.exe", ["/c", "git", "rev-parse", "HEAD"], { cwd, windowsHide: true }) : spawn("git", ["rev-parse", "HEAD"], { cwd, shell: false });
|
|
4294
4491
|
let stdout2 = "";
|
|
@@ -4297,15 +4494,15 @@ function gitRevParseHead(cwd, timeoutMs = 1e4) {
|
|
|
4297
4494
|
});
|
|
4298
4495
|
const timer = setTimeout(() => {
|
|
4299
4496
|
child.kill("SIGKILL");
|
|
4300
|
-
|
|
4497
|
+
resolve15({ sha: "", exit: -1 });
|
|
4301
4498
|
}, timeoutMs);
|
|
4302
4499
|
child.on("error", () => {
|
|
4303
4500
|
clearTimeout(timer);
|
|
4304
|
-
|
|
4501
|
+
resolve15({ sha: "", exit: -1 });
|
|
4305
4502
|
});
|
|
4306
4503
|
child.on("close", (code) => {
|
|
4307
4504
|
clearTimeout(timer);
|
|
4308
|
-
|
|
4505
|
+
resolve15({ sha: stdout2.trim(), exit: code ?? -1 });
|
|
4309
4506
|
});
|
|
4310
4507
|
});
|
|
4311
4508
|
}
|
|
@@ -6238,7 +6435,7 @@ var uninstallNpmCli = async (ctx) => {
|
|
|
6238
6435
|
const m = install.cmd.match(/npm\s+(?:install|i)\s+(?:-g\s+)?(\S+)/);
|
|
6239
6436
|
const pkg = m?.[1] ?? ctx.manifest.metadata.upstream.source;
|
|
6240
6437
|
const isWin = process.platform === "win32";
|
|
6241
|
-
const result = await new Promise((
|
|
6438
|
+
const result = await new Promise((resolve15) => {
|
|
6242
6439
|
const child = isWin ? spawn("cmd.exe", ["/c", "npm", "uninstall", "-g", pkg], { windowsHide: true }) : spawn("npm", ["uninstall", "-g", pkg], { shell: false });
|
|
6243
6440
|
let stderr = "";
|
|
6244
6441
|
child.stderr?.setEncoding("utf8").on("data", (c) => {
|
|
@@ -6246,15 +6443,15 @@ var uninstallNpmCli = async (ctx) => {
|
|
|
6246
6443
|
});
|
|
6247
6444
|
const timer = setTimeout(() => {
|
|
6248
6445
|
child.kill("SIGKILL");
|
|
6249
|
-
|
|
6446
|
+
resolve15({ exitCode: -1, stderr: `${stderr}[timeout]` });
|
|
6250
6447
|
}, 3e4);
|
|
6251
6448
|
child.on("error", (e) => {
|
|
6252
6449
|
clearTimeout(timer);
|
|
6253
|
-
|
|
6450
|
+
resolve15({ exitCode: -1, stderr: e.message });
|
|
6254
6451
|
});
|
|
6255
6452
|
child.on("close", (code) => {
|
|
6256
6453
|
clearTimeout(timer);
|
|
6257
|
-
|
|
6454
|
+
resolve15({ exitCode: code ?? -1, stderr });
|
|
6258
6455
|
});
|
|
6259
6456
|
});
|
|
6260
6457
|
if (result.exitCode !== 0) {
|