harnessed 2.0.1 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +246 -47
- package/dist/cli.mjs +416 -98
- package/dist/cli.mjs.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/workflows/capabilities.yaml +468 -0
- package/workflows/defaults.yaml +71 -4
- package/workflows/disciplines/karpathy.yaml +47 -0
- package/workflows/disciplines/language.yaml +38 -0
- package/workflows/disciplines/operational.yaml +61 -0
- package/workflows/disciplines/output-style.yaml +62 -0
- package/workflows/disciplines/priority.yaml +28 -0
- package/workflows/disciplines/protocols.yaml +70 -0
- package/workflows/discuss/auto/.gitkeep +0 -0
- package/workflows/discuss/auto/SKILL.md +63 -0
- package/workflows/discuss/auto/workflow.yaml +40 -0
- package/workflows/discuss/phase/SKILL.md +61 -0
- package/workflows/discuss/phase/workflow.yaml +35 -0
- package/workflows/discuss/strategic/SKILL.md +66 -0
- package/workflows/discuss/strategic/workflow.yaml +47 -0
- package/workflows/discuss/subtask/SKILL.md +67 -0
- package/workflows/discuss/subtask/workflow.yaml +33 -0
- package/workflows/judgments/stage-routing.yaml +93 -0
- package/workflows/judgments/web-design-routing.yaml +37 -0
- package/workflows/judgments/web-search-routing.yaml +52 -0
- package/workflows/judgments/web-testing-routing.yaml +50 -0
- package/workflows/plan/architecture/SKILL.md +62 -0
- package/workflows/plan/architecture/workflow.yaml +33 -0
- package/workflows/plan/auto/.gitkeep +0 -0
- package/workflows/plan/auto/SKILL.md +63 -0
- package/workflows/plan/auto/workflow.yaml +41 -0
- package/workflows/plan/phase/SKILL.md +64 -0
- package/workflows/plan/phase/workflow.yaml +37 -0
- package/workflows/research/SKILL.md +6 -2
- package/workflows/research/workflow.yaml +34 -3
- package/workflows/retro/SKILL.md +68 -0
- package/workflows/retro/workflow.yaml +40 -0
- package/workflows/task/auto/.gitkeep +0 -0
- package/workflows/task/auto/SKILL.md +68 -0
- package/workflows/task/auto/workflow.yaml +57 -0
- package/workflows/task/clarify/SKILL.md +83 -0
- package/workflows/task/clarify/workflow.yaml +39 -0
- package/workflows/task/code/SKILL.md +89 -0
- package/workflows/task/code/workflow.yaml +55 -0
- package/workflows/task/deliver/SKILL.md +118 -0
- package/workflows/task/deliver/workflow.yaml +77 -0
- package/workflows/task/test/SKILL.md +93 -0
- package/workflows/task/test/workflow.yaml +44 -0
- package/workflows/verify/auto/.gitkeep +0 -0
- package/workflows/verify/auto/SKILL.md +77 -0
- package/workflows/verify/auto/workflow.yaml +74 -0
- package/workflows/verify/code-review/SKILL.md +69 -0
- package/workflows/verify/code-review/workflow.yaml +32 -0
- package/workflows/verify/design/SKILL.md +72 -0
- package/workflows/verify/design/workflow.yaml +33 -0
- package/workflows/verify/multispec/SKILL.md +86 -0
- package/workflows/verify/multispec/workflow.yaml +58 -0
- package/workflows/verify/paranoid/SKILL.md +71 -0
- package/workflows/verify/paranoid/workflow.yaml +30 -0
- package/workflows/verify/progress/SKILL.md +67 -0
- package/workflows/verify/progress/workflow.yaml +44 -0
- package/workflows/verify/qa/SKILL.md +73 -0
- package/workflows/verify/qa/workflow.yaml +31 -0
- package/workflows/verify/security/SKILL.md +67 -0
- package/workflows/verify/security/workflow.yaml +31 -0
- package/workflows/verify/simplify/SKILL.md +67 -0
- package/workflows/verify/simplify/workflow.yaml +31 -0
package/dist/cli.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { spawnSync, spawn } from 'child_process';
|
|
2
|
+
import { execSync, spawnSync, spawn } from 'child_process';
|
|
3
3
|
import { writeFileSync, existsSync, readFileSync, mkdirSync, appendFileSync, readdirSync } from 'fs';
|
|
4
4
|
import { resolve, join, dirname, relative } from 'path';
|
|
5
5
|
import { Type } from '@sinclair/typebox';
|
|
@@ -169,17 +169,21 @@ var init_schemaVersion = __esm({
|
|
|
169
169
|
governance: "harnessed.governance.v1",
|
|
170
170
|
// ← Phase 3.2 W1 T1.1 ADD 10th surface (D-04 PUSH veto status)
|
|
171
171
|
planFeature: "harnessed.plan-feature.v1",
|
|
172
|
-
// ← Phase 3.3 W0 T0.5 BACKFILL 11th surface
|
|
172
|
+
// ← Phase 3.3 W0 T0.5 BACKFILL 11th surface
|
|
173
173
|
aliases: "harnessed.aliases.v1",
|
|
174
|
-
// ← Phase 3.3 W1 T1.1 ADD 12th surface (D-01 RICH
|
|
174
|
+
// ← Phase 3.3 W1 T1.1 ADD 12th surface (D-01 RICH)
|
|
175
175
|
knownGood: "harnessed.known-good.v1",
|
|
176
|
-
// ← Phase 3.3 W1 T1.1 ADD 13th surface (D-03 YAML
|
|
176
|
+
// ← Phase 3.3 W1 T1.1 ADD 13th surface (D-03 YAML manifest)
|
|
177
177
|
capabilities: "harnessed.capabilities.v1",
|
|
178
|
-
// ← Phase v2.0-2.3 W0
|
|
178
|
+
// ← Phase v2.0-2.3 W0 ADD 14th surface (R20.2 flat yaml capabilities manifest validate)
|
|
179
179
|
judgment: "harnessed.judgment.v1",
|
|
180
|
-
// ← Phase v2.0-2.3 W0
|
|
181
|
-
workflow: "harnessed.workflow.v2"
|
|
182
|
-
// ← Phase v2.0-2.4 W0 T2.4.W0.1 ADD 16th surface (R20.1
|
|
180
|
+
// ← Phase v2.0-2.3 W0 ADD 15th surface (R20.4 multi-file judgments validate)
|
|
181
|
+
workflow: "harnessed.workflow.v2",
|
|
182
|
+
// ← Phase v2.0-2.4 W0 T2.4.W0.1 ADD 16th surface (R20.1+R20.2+R20.9 workflow.yaml v2)
|
|
183
|
+
workflow_v3: "harnessed.workflow.v3",
|
|
184
|
+
// ← Phase v3.0-3.3 W0 T3.3.W0.11 ADD 17th surface (D-09 disciplines_applied + D-05 tools_available + master delegates_to per Pattern A B.1 LOCK)
|
|
185
|
+
discipline: "harnessed.discipline.v1"
|
|
186
|
+
// ← Phase v3.0-3.3 W0 T3.3.W0.11 ADD 18th surface (D-09 L0 Discipline Substrate, sister judgment.v1 multi-file pattern)
|
|
183
187
|
};
|
|
184
188
|
Type.Union([
|
|
185
189
|
Type.Literal(SCHEMA_VERSIONS.routingSnapshot),
|
|
@@ -202,11 +206,15 @@ var init_schemaVersion = __esm({
|
|
|
202
206
|
Type.Literal(SCHEMA_VERSIONS.knownGood),
|
|
203
207
|
// ← Phase 3.3 W1 T1.1 ADD 13th surface
|
|
204
208
|
Type.Literal(SCHEMA_VERSIONS.capabilities),
|
|
205
|
-
// ← Phase v2.0-2.3 W0
|
|
209
|
+
// ← Phase v2.0-2.3 W0 ADD 14th surface
|
|
206
210
|
Type.Literal(SCHEMA_VERSIONS.judgment),
|
|
207
|
-
// ← Phase v2.0-2.3 W0
|
|
208
|
-
Type.Literal(SCHEMA_VERSIONS.workflow)
|
|
209
|
-
// ← Phase v2.0-2.4 W0 T2.4.W0.1 ADD 16th surface (
|
|
211
|
+
// ← Phase v2.0-2.3 W0 ADD 15th surface
|
|
212
|
+
Type.Literal(SCHEMA_VERSIONS.workflow),
|
|
213
|
+
// ← Phase v2.0-2.4 W0 T2.4.W0.1 ADD 16th surface (first .v2 in union)
|
|
214
|
+
Type.Literal(SCHEMA_VERSIONS.workflow_v3),
|
|
215
|
+
// ← Phase v3.0-3.3 W0 T3.3.W0.11 ADD 17th surface (first .v3 in union)
|
|
216
|
+
Type.Literal(SCHEMA_VERSIONS.discipline)
|
|
217
|
+
// ← Phase v3.0-3.3 W0 T3.3.W0.11 ADD 18th surface
|
|
210
218
|
]);
|
|
211
219
|
}
|
|
212
220
|
});
|
|
@@ -692,10 +700,10 @@ __export(knownGood_exports, {
|
|
|
692
700
|
loadKnownGood: () => loadKnownGood
|
|
693
701
|
});
|
|
694
702
|
function loadKnownGood(harnessedVer) {
|
|
695
|
-
if (
|
|
703
|
+
if (_cache2.has(harnessedVer)) return _cache2.get(harnessedVer) ?? null;
|
|
696
704
|
const path = join(versionsDir(), `${harnessedVer}-known-good.yaml`);
|
|
697
705
|
if (!existsSync(path)) {
|
|
698
|
-
|
|
706
|
+
_cache2.set(harnessedVer, null);
|
|
699
707
|
return null;
|
|
700
708
|
}
|
|
701
709
|
const raw = readFileSync(path, "utf8");
|
|
@@ -706,7 +714,7 @@ function loadKnownGood(harnessedVer) {
|
|
|
706
714
|
`${path} schema invalid: ${errs.map((e) => `${e.path} ${e.message}`).join("; ")}`
|
|
707
715
|
);
|
|
708
716
|
}
|
|
709
|
-
|
|
717
|
+
_cache2.set(harnessedVer, parsed);
|
|
710
718
|
return parsed;
|
|
711
719
|
}
|
|
712
720
|
function getPinnedVersion(upstreamName, harnessedVer) {
|
|
@@ -715,12 +723,12 @@ function getPinnedVersion(upstreamName, harnessedVer) {
|
|
|
715
723
|
const entry = kg.upstreams.find((u) => u.name === upstreamName);
|
|
716
724
|
return entry?.version ?? null;
|
|
717
725
|
}
|
|
718
|
-
var versionsDir,
|
|
726
|
+
var versionsDir, _cache2;
|
|
719
727
|
var init_knownGood = __esm({
|
|
720
728
|
"src/manifest/knownGood.ts"() {
|
|
721
729
|
init_known_good_v1();
|
|
722
730
|
versionsDir = () => join(process.cwd(), "versions");
|
|
723
|
-
|
|
731
|
+
_cache2 = /* @__PURE__ */ new Map();
|
|
724
732
|
}
|
|
725
733
|
});
|
|
726
734
|
|
|
@@ -785,7 +793,7 @@ var init_resume = __esm({
|
|
|
785
793
|
|
|
786
794
|
// package.json
|
|
787
795
|
var package_default = {
|
|
788
|
-
version: "
|
|
796
|
+
version: "3.0.1"};
|
|
789
797
|
|
|
790
798
|
// src/manifest/errors.ts
|
|
791
799
|
function instancePathToKeyPath(instancePath) {
|
|
@@ -1566,7 +1574,7 @@ function renderHumanTable(records) {
|
|
|
1566
1574
|
}
|
|
1567
1575
|
}
|
|
1568
1576
|
function pipeToJq(filterExpr, lines) {
|
|
1569
|
-
return new Promise((
|
|
1577
|
+
return new Promise((resolve9, reject) => {
|
|
1570
1578
|
const child = spawn("jq", [filterExpr], {
|
|
1571
1579
|
stdio: ["pipe", "inherit", "inherit"],
|
|
1572
1580
|
windowsHide: true
|
|
@@ -1575,12 +1583,12 @@ function pipeToJq(filterExpr, lines) {
|
|
|
1575
1583
|
const e = err2;
|
|
1576
1584
|
if (e.code === "ENOENT") {
|
|
1577
1585
|
console.error("\u2717 jq not found in PATH \u2014 run: harnessed doctor");
|
|
1578
|
-
|
|
1586
|
+
resolve9(1);
|
|
1579
1587
|
} else {
|
|
1580
1588
|
reject(err2);
|
|
1581
1589
|
}
|
|
1582
1590
|
});
|
|
1583
|
-
child.on("close", (code) =>
|
|
1591
|
+
child.on("close", (code) => resolve9(code ?? 0));
|
|
1584
1592
|
child.stdin.write(lines.join("\n"));
|
|
1585
1593
|
child.stdin.end();
|
|
1586
1594
|
});
|
|
@@ -1932,6 +1940,107 @@ function registerDoctor(program2) {
|
|
|
1932
1940
|
});
|
|
1933
1941
|
}
|
|
1934
1942
|
|
|
1943
|
+
// src/workflow/schema/discipline.ts
|
|
1944
|
+
init_schemaVersion();
|
|
1945
|
+
var EnforcementLayer = Type.Union([
|
|
1946
|
+
Type.Literal("code-writing"),
|
|
1947
|
+
// karpathy 心法 — write code phase
|
|
1948
|
+
Type.Literal("output"),
|
|
1949
|
+
// BLUF / language / no-emoji — emit response phase
|
|
1950
|
+
Type.Literal("commit"),
|
|
1951
|
+
// biome / A7 / commit safety — pre-commit phase
|
|
1952
|
+
Type.Literal("workflow"),
|
|
1953
|
+
// priority hierarchy / protocols — workflow-level arbitration
|
|
1954
|
+
Type.Literal("tool")
|
|
1955
|
+
// tool invoke discipline (reserved for v3.x extension)
|
|
1956
|
+
]);
|
|
1957
|
+
var Enforcement = Type.Union([
|
|
1958
|
+
Type.Literal("halt"),
|
|
1959
|
+
// process.exit non-zero, sister fallbackHandlers
|
|
1960
|
+
Type.Literal("warn"),
|
|
1961
|
+
// console.warn emit, continue
|
|
1962
|
+
Type.Literal("auto-fix"),
|
|
1963
|
+
// run auto_fix_cmd then continue (biome --write pattern)
|
|
1964
|
+
Type.Literal("info")
|
|
1965
|
+
// log only, no action
|
|
1966
|
+
]);
|
|
1967
|
+
var DisciplineRule = Type.Object(
|
|
1968
|
+
{
|
|
1969
|
+
id: Type.String({ minLength: 1 }),
|
|
1970
|
+
// kebab-case
|
|
1971
|
+
description: Type.String(),
|
|
1972
|
+
// human-readable
|
|
1973
|
+
enforcement: Enforcement,
|
|
1974
|
+
trigger: Type.Union([Type.String(), Type.Array(Type.String())]),
|
|
1975
|
+
// expr OR always-on list
|
|
1976
|
+
check_method: Type.String(),
|
|
1977
|
+
// heuristic / regex / external-cmd / llm-judge / file-content-match
|
|
1978
|
+
auto_fix_cmd: Type.Optional(Type.String())
|
|
1979
|
+
// only enforcement=auto-fix
|
|
1980
|
+
},
|
|
1981
|
+
{ additionalProperties: false }
|
|
1982
|
+
);
|
|
1983
|
+
var PriorityHierarchy = Type.Array(Type.String(), { minItems: 1 });
|
|
1984
|
+
var ProtocolShape = Type.Object(
|
|
1985
|
+
{
|
|
1986
|
+
description: Type.String(),
|
|
1987
|
+
required_fields: Type.Optional(Type.Array(Type.String())),
|
|
1988
|
+
forbidden_phrases: Type.Optional(Type.Array(Type.String())),
|
|
1989
|
+
file_ownership: Type.Optional(Type.Record(Type.String(), Type.Array(Type.String()))),
|
|
1990
|
+
rules: Type.Optional(Type.Array(DisciplineRule))
|
|
1991
|
+
},
|
|
1992
|
+
{ additionalProperties: false }
|
|
1993
|
+
);
|
|
1994
|
+
var Discipline = Type.Object(
|
|
1995
|
+
{
|
|
1996
|
+
schema_version: Type.Literal(SCHEMA_VERSIONS.discipline),
|
|
1997
|
+
discipline: Type.String({ minLength: 1 }),
|
|
1998
|
+
// basename (karpathy / output-style / ...)
|
|
1999
|
+
enforcement_layer: EnforcementLayer,
|
|
2000
|
+
auto_enforce: Type.Boolean(),
|
|
2001
|
+
rules: Type.Array(DisciplineRule),
|
|
2002
|
+
priority_hierarchy: Type.Optional(PriorityHierarchy),
|
|
2003
|
+
// priority.yaml only
|
|
2004
|
+
protocols: Type.Optional(Type.Record(Type.String(), ProtocolShape))
|
|
2005
|
+
// protocols.yaml only
|
|
2006
|
+
},
|
|
2007
|
+
{ additionalProperties: false }
|
|
2008
|
+
);
|
|
2009
|
+
|
|
2010
|
+
// src/workflow/disciplineLoader.ts
|
|
2011
|
+
var _cache = /* @__PURE__ */ new Map();
|
|
2012
|
+
async function loadDiscipline(basename2, packageRoot) {
|
|
2013
|
+
const cached = _cache.get(basename2);
|
|
2014
|
+
if (cached) return cached;
|
|
2015
|
+
const yamlPath = resolve(packageRoot, "workflows", "disciplines", `${basename2}.yaml`);
|
|
2016
|
+
const raw = await readFile(yamlPath, "utf8");
|
|
2017
|
+
const parsedRaw = parse(raw);
|
|
2018
|
+
if (!Value.Check(Discipline, parsedRaw)) {
|
|
2019
|
+
const errors = [...Value.Errors(Discipline, parsedRaw)].slice(0, 3).map((e) => `${e.path} ${e.message}`).join("; ");
|
|
2020
|
+
throw new Error(`Invalid discipline file ${basename2}.yaml: ${errors}`);
|
|
2021
|
+
}
|
|
2022
|
+
const parsed = parsedRaw;
|
|
2023
|
+
_cache.set(basename2, parsed);
|
|
2024
|
+
return parsed;
|
|
2025
|
+
}
|
|
2026
|
+
|
|
2027
|
+
// src/discipline/enforcement/before-commit.ts
|
|
2028
|
+
var TS_JS_RE = /\.(ts|tsx|js|mjs)$/;
|
|
2029
|
+
async function runBeforeCommitHook(ctx) {
|
|
2030
|
+
const d = await loadDiscipline("operational", ctx.packageRoot);
|
|
2031
|
+
if (ctx.changedFiles.some((f) => TS_JS_RE.test(f))) {
|
|
2032
|
+
const rule = d.rules.find((r) => r.id === "biome-preempt");
|
|
2033
|
+
if (rule?.auto_fix_cmd) {
|
|
2034
|
+
console.warn("\u26A0\uFE0F biome preempt \u2014 running auto-fix before commit");
|
|
2035
|
+
execSync(rule.auto_fix_cmd, { cwd: ctx.packageRoot, stdio: "inherit" });
|
|
2036
|
+
}
|
|
2037
|
+
}
|
|
2038
|
+
if (ctx.cmdArgs.includes("--no-verify")) {
|
|
2039
|
+
console.error("\u274C no-skip-hooks violated: --no-verify forbidden");
|
|
2040
|
+
process.exit(2);
|
|
2041
|
+
}
|
|
2042
|
+
}
|
|
2043
|
+
|
|
1935
2044
|
// src/routing/completionSchema.ts
|
|
1936
2045
|
var COMPLETION_SCHEMA = {
|
|
1937
2046
|
type: "object",
|
|
@@ -2636,15 +2745,100 @@ var PhasesSchema = Type.Object(
|
|
|
2636
2745
|
|
|
2637
2746
|
// src/workflow/schema/workflow.ts
|
|
2638
2747
|
init_schemaVersion();
|
|
2748
|
+
|
|
2749
|
+
// src/workflow/schema/workflow.v2.ts
|
|
2750
|
+
init_schemaVersion();
|
|
2639
2751
|
var ModelTier2 = Type.Union([Type.Literal("haiku"), Type.Literal("sonnet"), Type.Literal("opus")]);
|
|
2640
2752
|
var OnAction = Type.Union([Type.Literal("skip"), Type.Literal("invoke")]);
|
|
2753
|
+
var OnClauseV2 = Type.Object(
|
|
2754
|
+
{
|
|
2755
|
+
if: Type.String(),
|
|
2756
|
+
invoke: Type.Optional(Type.String()),
|
|
2757
|
+
action: Type.Optional(OnAction)
|
|
2758
|
+
},
|
|
2759
|
+
{ additionalProperties: false }
|
|
2760
|
+
);
|
|
2761
|
+
var FallbackMaxIterationsExceededV2 = Type.Object(
|
|
2762
|
+
{
|
|
2763
|
+
action: Type.Literal("emit_warning_and_halt"),
|
|
2764
|
+
message: Type.String(),
|
|
2765
|
+
exit_code: Type.Number()
|
|
2766
|
+
},
|
|
2767
|
+
{ additionalProperties: false }
|
|
2768
|
+
);
|
|
2769
|
+
var PhaseFallbackV2 = Type.Object(
|
|
2770
|
+
{
|
|
2771
|
+
max_iterations_exceeded: Type.Optional(FallbackMaxIterationsExceededV2)
|
|
2772
|
+
},
|
|
2773
|
+
{ additionalProperties: false }
|
|
2774
|
+
);
|
|
2775
|
+
var WorkflowPhaseV2 = Type.Object(
|
|
2776
|
+
{
|
|
2777
|
+
id: Type.String({ minLength: 1 }),
|
|
2778
|
+
name: Type.Optional(Type.String()),
|
|
2779
|
+
upstream: Type.Optional(Type.String()),
|
|
2780
|
+
capability: Type.Optional(Type.String()),
|
|
2781
|
+
model: Type.Optional(ModelTier2),
|
|
2782
|
+
invokes: Type.Optional(Type.String()),
|
|
2783
|
+
args: Type.Optional(Type.Record(Type.String(), Type.Unknown())),
|
|
2784
|
+
gate: Type.Optional(Type.String()),
|
|
2785
|
+
on: Type.Optional(Type.Array(OnClauseV2)),
|
|
2786
|
+
parallelism: Type.Optional(Type.String()),
|
|
2787
|
+
fallback: Type.Optional(PhaseFallbackV2),
|
|
2788
|
+
max_iterations: Type.Optional(Type.Union([Type.Number(), Type.String()])),
|
|
2789
|
+
artifacts_expected: Type.Optional(Type.Array(Type.String()))
|
|
2790
|
+
},
|
|
2791
|
+
{ additionalProperties: false }
|
|
2792
|
+
);
|
|
2793
|
+
var WorkflowSchemaV2 = Type.Object(
|
|
2794
|
+
{
|
|
2795
|
+
schema_version: Type.Literal(SCHEMA_VERSIONS.workflow),
|
|
2796
|
+
workflow: Type.String({ minLength: 1 }),
|
|
2797
|
+
description: Type.Optional(Type.String()),
|
|
2798
|
+
phases: Type.Array(WorkflowPhaseV2, { minItems: 1 })
|
|
2799
|
+
},
|
|
2800
|
+
{ additionalProperties: false }
|
|
2801
|
+
);
|
|
2802
|
+
|
|
2803
|
+
// src/workflow/schema/workflow.ts
|
|
2804
|
+
var ModelTier3 = Type.Union([Type.Literal("haiku"), Type.Literal("sonnet"), Type.Literal("opus")]);
|
|
2805
|
+
var OnAction2 = Type.Union([Type.Literal("skip"), Type.Literal("invoke")]);
|
|
2806
|
+
var DisciplineName = Type.Union([
|
|
2807
|
+
Type.Literal("karpathy"),
|
|
2808
|
+
Type.Literal("output-style"),
|
|
2809
|
+
Type.Literal("language"),
|
|
2810
|
+
Type.Literal("operational"),
|
|
2811
|
+
Type.Literal("priority"),
|
|
2812
|
+
Type.Literal("protocols")
|
|
2813
|
+
]);
|
|
2641
2814
|
var OnClause = Type.Object(
|
|
2642
2815
|
{
|
|
2643
2816
|
if: Type.String(),
|
|
2644
2817
|
// expr-eval expression OR judgments.<file>.<gate>.fires ref
|
|
2645
2818
|
invoke: Type.Optional(Type.String()),
|
|
2646
2819
|
// '{{ capabilities.<name>.cmd }}' OR literal
|
|
2647
|
-
action: Type.Optional(
|
|
2820
|
+
action: Type.Optional(OnAction2)
|
|
2821
|
+
},
|
|
2822
|
+
{ additionalProperties: false }
|
|
2823
|
+
);
|
|
2824
|
+
var InvokeToolClause = Type.Object(
|
|
2825
|
+
{
|
|
2826
|
+
if: Type.Optional(Type.String()),
|
|
2827
|
+
// optional — 无 if = unconditional fire
|
|
2828
|
+
tool: Type.String({ minLength: 1 })
|
|
2829
|
+
// capabilities.yaml entry name (cross-validate T3.3.W0.10)
|
|
2830
|
+
},
|
|
2831
|
+
{ additionalProperties: false }
|
|
2832
|
+
);
|
|
2833
|
+
var DelegationClause = Type.Object(
|
|
2834
|
+
{
|
|
2835
|
+
sub: Type.String({ minLength: 1 }),
|
|
2836
|
+
// sub-stage workflow name e.g. 'strategic' / 'phase' / 'subtask'
|
|
2837
|
+
gate: Type.Optional(Type.String()),
|
|
2838
|
+
// judgments.<file>.<trigger>.fires 4-level ref
|
|
2839
|
+
mode: Type.Optional(Type.Union([Type.Literal("parallel"), Type.Literal("serial")])),
|
|
2840
|
+
order: Type.Optional(Type.Number())
|
|
2841
|
+
// serial-only: explicit ordering (K9 mitigation enforced in check-workflow-schema)
|
|
2648
2842
|
},
|
|
2649
2843
|
{ additionalProperties: false }
|
|
2650
2844
|
);
|
|
@@ -2663,14 +2857,14 @@ var PhaseFallback = Type.Object(
|
|
|
2663
2857
|
},
|
|
2664
2858
|
{ additionalProperties: false }
|
|
2665
2859
|
);
|
|
2666
|
-
var
|
|
2860
|
+
var WorkflowPhaseV3 = Type.Object(
|
|
2667
2861
|
{
|
|
2668
2862
|
id: Type.String({ minLength: 1 }),
|
|
2669
2863
|
name: Type.Optional(Type.String()),
|
|
2670
2864
|
upstream: Type.Optional(Type.String()),
|
|
2671
2865
|
capability: Type.Optional(Type.String()),
|
|
2672
2866
|
// '{{ capabilities.ralph-loop.cmd }}'
|
|
2673
|
-
model: Type.Optional(
|
|
2867
|
+
model: Type.Optional(ModelTier3),
|
|
2674
2868
|
invokes: Type.Optional(Type.String()),
|
|
2675
2869
|
// legacy slash-cmd OR JINJA template
|
|
2676
2870
|
args: Type.Optional(Type.Record(Type.String(), Type.Unknown())),
|
|
@@ -2684,16 +2878,25 @@ var WorkflowPhaseV2 = Type.Object(
|
|
|
2684
2878
|
Type.Union([Type.Number(), Type.String()])
|
|
2685
2879
|
// numeric literal OR jinja '{{ defaults.x.y }}'
|
|
2686
2880
|
),
|
|
2687
|
-
artifacts_expected: Type.Optional(Type.Array(Type.String()))
|
|
2881
|
+
artifacts_expected: Type.Optional(Type.Array(Type.String())),
|
|
2882
|
+
invokes_tools: Type.Optional(Type.Array(InvokeToolClause))
|
|
2883
|
+
// NEW v3 D-05 phase-level conditional fire
|
|
2688
2884
|
},
|
|
2689
2885
|
{ additionalProperties: false }
|
|
2690
2886
|
);
|
|
2691
|
-
|
|
2887
|
+
Type.Object(
|
|
2692
2888
|
{
|
|
2693
|
-
schema_version: Type.Literal(SCHEMA_VERSIONS.
|
|
2889
|
+
schema_version: Type.Literal(SCHEMA_VERSIONS.workflow_v3),
|
|
2694
2890
|
workflow: Type.String({ minLength: 1 }),
|
|
2695
2891
|
description: Type.Optional(Type.String()),
|
|
2696
|
-
|
|
2892
|
+
disciplines_applied: Type.Optional(Type.Array(DisciplineName)),
|
|
2893
|
+
// NEW v3 D-09 (Pattern A A.1 strict Literal Union)
|
|
2894
|
+
tools_available: Type.Optional(Type.Array(Type.String())),
|
|
2895
|
+
// NEW v3 D-05 (cross-validate T3.3.W0.10)
|
|
2896
|
+
delegates_to: Type.Optional(Type.Array(DelegationClause)),
|
|
2897
|
+
// NEW v3 D-01 (master orchestrator only)
|
|
2898
|
+
phases: Type.Optional(Type.Array(WorkflowPhaseV3, { minItems: 1 }))
|
|
2899
|
+
// 改 Optional — master 无 phases, sub/standalone 必有
|
|
2697
2900
|
},
|
|
2698
2901
|
{ additionalProperties: false }
|
|
2699
2902
|
);
|
|
@@ -2737,7 +2940,12 @@ function validateNonInteractiveFlags(raw, cmdName) {
|
|
|
2737
2940
|
|
|
2738
2941
|
// src/cli/execute-task.ts
|
|
2739
2942
|
function registerExecuteTask(program2) {
|
|
2740
|
-
program2.command("execute-task").description(
|
|
2943
|
+
program2.command("execute-task").description(
|
|
2944
|
+
"Run execute-task workflow (4-phase chain \u2192 ralph-loop COMPLETE; immediate by default \u2014 use --dry-run for preview)"
|
|
2945
|
+
).requiredOption("--task <text>", "task description (required)").option("--workflow <name>", "workflow name", "execute-task").option(
|
|
2946
|
+
"--apply",
|
|
2947
|
+
"(deprecated; kept for backward compat \u2014 execute-task spawns immediately by default)"
|
|
2948
|
+
).option("--dry-run", "preview only \u2014 do not spawn subagent (opt-in for advanced users)").option("--non-interactive", "CI / scripts \u2014 requires --apply or --dry-run").option("--model <model>", "subagent model: 'haiku' | 'sonnet' | 'opus'").option("--model-tier <tier>", "override: 'inherit' bypasses per-phase phase.model (B-10)").option("--max-iterations <n>", "ralph-loop max iter (default 20)", (v) => parseInt(v, 10)).action(async (raw) => {
|
|
2741
2949
|
validateNonInteractiveFlags(raw, "execute-task --task <text>");
|
|
2742
2950
|
if (!raw.task) {
|
|
2743
2951
|
console.error("error: --task <text> is required");
|
|
@@ -2760,7 +2968,7 @@ function registerExecuteTask(program2) {
|
|
|
2760
2968
|
};
|
|
2761
2969
|
}
|
|
2762
2970
|
const taskCtx = { task: raw.task, task_type: "execute-task" };
|
|
2763
|
-
const isDryRun = raw.dryRun === true
|
|
2971
|
+
const isDryRun = raw.dryRun === true;
|
|
2764
2972
|
if (isDryRun) {
|
|
2765
2973
|
console.log(
|
|
2766
2974
|
JSON.stringify({ workflow: phases.workflow, phases: phases.phases, taskCtx }, null, 2)
|
|
@@ -2776,6 +2984,22 @@ function registerExecuteTask(program2) {
|
|
|
2776
2984
|
break;
|
|
2777
2985
|
}
|
|
2778
2986
|
}
|
|
2987
|
+
try {
|
|
2988
|
+
const stagedOut = execSync("git status --porcelain", { encoding: "utf8" });
|
|
2989
|
+
const changedFiles = stagedOut.split("\n").filter((l) => l.trim().length > 0).map((l) => l.slice(3).trim());
|
|
2990
|
+
await runBeforeCommitHook({
|
|
2991
|
+
changedFiles,
|
|
2992
|
+
cmdArgs: [],
|
|
2993
|
+
packageRoot: process.cwd(),
|
|
2994
|
+
cmdType: "git-commit",
|
|
2995
|
+
hasUserApproval: true
|
|
2996
|
+
// --apply explicit
|
|
2997
|
+
});
|
|
2998
|
+
} catch (err2) {
|
|
2999
|
+
console.warn(
|
|
3000
|
+
`\u26A0\uFE0F before-commit pre-flight skipped (${err2.message}); subagent will biome-check at commit time.`
|
|
3001
|
+
);
|
|
3002
|
+
}
|
|
2779
3003
|
const result = await runRouting(taskCtx, {
|
|
2780
3004
|
maxIterations: raw.maxIterations ?? 20,
|
|
2781
3005
|
...raw.model ? { agentOpts: { modelOverride: raw.model } } : {},
|
|
@@ -2822,8 +3046,10 @@ async function dirSizeKb(dir) {
|
|
|
2822
3046
|
return Math.round(total / 1024);
|
|
2823
3047
|
}
|
|
2824
3048
|
function registerGc(program2) {
|
|
2825
|
-
program2.command("gc").description(
|
|
2826
|
-
|
|
3049
|
+
program2.command("gc").description(
|
|
3050
|
+
"Garbage-collect old backup snapshots (immediate by default \u2014 use --dry-run for preview)"
|
|
3051
|
+
).option("--older-than <duration>", "delete snapshots older than (e.g. 30d / 24h / 4w)", "30d").option("--keep-last <N>", "always keep the most recent N snapshots", "0").option("--apply", "(deprecated; kept for backward compat \u2014 gc deletes immediately by default)").option("--dry-run", "preview only \u2014 do not delete (opt-in for advanced users)").action(async (opts) => {
|
|
3052
|
+
const dryRun = opts.dryRun === true;
|
|
2827
3053
|
const olderMs = parseDuration(opts.olderThan ?? "30d");
|
|
2828
3054
|
if (olderMs == null) {
|
|
2829
3055
|
console.error(
|
|
@@ -2873,7 +3099,7 @@ function registerGc(program2) {
|
|
|
2873
3099
|
console.log(` ${c.ts} ${c.manifest} (${c.sizeKb} KB)`);
|
|
2874
3100
|
if (!dryRun) await rm(c.path, { recursive: true, force: true });
|
|
2875
3101
|
}
|
|
2876
|
-
if (dryRun) console.log("\n(dry-run \u2014 re-run
|
|
3102
|
+
if (dryRun) console.log("\n(dry-run \u2014 re-run without --dry-run to actually delete)");
|
|
2877
3103
|
});
|
|
2878
3104
|
}
|
|
2879
3105
|
async function confirmAt(level, ctx) {
|
|
@@ -3157,7 +3383,7 @@ var installCcHookAdd = async (ctx) => {
|
|
|
3157
3383
|
return { ok: true, backupId: bk.backupId, appliedFiles: [settingsPath] };
|
|
3158
3384
|
};
|
|
3159
3385
|
function runArgs(claudeArgs, cwd, timeoutMs = 15e3) {
|
|
3160
|
-
return new Promise((
|
|
3386
|
+
return new Promise((resolve9) => {
|
|
3161
3387
|
const isWin = process.platform === "win32";
|
|
3162
3388
|
const child = isWin ? spawn("cmd.exe", ["/c", "claude", ...claudeArgs], { cwd, windowsHide: true }) : spawn("claude", claudeArgs, { cwd, shell: false });
|
|
3163
3389
|
let stderr = "";
|
|
@@ -3166,15 +3392,15 @@ function runArgs(claudeArgs, cwd, timeoutMs = 15e3) {
|
|
|
3166
3392
|
});
|
|
3167
3393
|
const timer = setTimeout(() => {
|
|
3168
3394
|
child.kill("SIGKILL");
|
|
3169
|
-
|
|
3395
|
+
resolve9({ exitCode: -1, stderr: `${stderr}[timeout after ${timeoutMs}ms]` });
|
|
3170
3396
|
}, timeoutMs);
|
|
3171
3397
|
child.on("error", (e) => {
|
|
3172
3398
|
clearTimeout(timer);
|
|
3173
|
-
|
|
3399
|
+
resolve9({ exitCode: -1, stderr: `${stderr}${e.message}` });
|
|
3174
3400
|
});
|
|
3175
3401
|
child.on("close", (code) => {
|
|
3176
3402
|
clearTimeout(timer);
|
|
3177
|
-
|
|
3403
|
+
resolve9({ exitCode: code ?? -1, stderr });
|
|
3178
3404
|
});
|
|
3179
3405
|
});
|
|
3180
3406
|
}
|
|
@@ -3315,7 +3541,7 @@ ${newEntry}
|
|
|
3315
3541
|
)
|
|
3316
3542
|
};
|
|
3317
3543
|
}
|
|
3318
|
-
const vr = await new Promise((
|
|
3544
|
+
const vr = await new Promise((resolve9) => {
|
|
3319
3545
|
const child = spawn(verifyShell, [verifyFlag, verifyLine], { cwd: ctx.cwd, windowsHide: true });
|
|
3320
3546
|
let stderr = "";
|
|
3321
3547
|
child.stderr?.setEncoding("utf8").on("data", (c) => {
|
|
@@ -3323,15 +3549,15 @@ ${newEntry}
|
|
|
3323
3549
|
});
|
|
3324
3550
|
const timer = setTimeout(() => {
|
|
3325
3551
|
child.kill("SIGKILL");
|
|
3326
|
-
|
|
3552
|
+
resolve9({ exitCode: -1, stderr: `${stderr}[timeout]` });
|
|
3327
3553
|
}, 15e3);
|
|
3328
3554
|
child.on("error", (e) => {
|
|
3329
3555
|
clearTimeout(timer);
|
|
3330
|
-
|
|
3556
|
+
resolve9({ exitCode: -1, stderr: e.message });
|
|
3331
3557
|
});
|
|
3332
3558
|
child.on("close", (code) => {
|
|
3333
3559
|
clearTimeout(timer);
|
|
3334
|
-
|
|
3560
|
+
resolve9({ exitCode: code ?? -1, stderr });
|
|
3335
3561
|
});
|
|
3336
3562
|
});
|
|
3337
3563
|
if (vr.exitCode !== 0) {
|
|
@@ -3387,10 +3613,10 @@ async function spawnCmd(ctx, cmd, args) {
|
|
|
3387
3613
|
child.stderr?.setEncoding("utf8").on("data", (chunk) => {
|
|
3388
3614
|
stderr += chunk;
|
|
3389
3615
|
});
|
|
3390
|
-
return await new Promise((
|
|
3616
|
+
return await new Promise((resolve9) => {
|
|
3391
3617
|
const timer = setTimeout(() => {
|
|
3392
3618
|
child.kill("SIGKILL");
|
|
3393
|
-
|
|
3619
|
+
resolve9({
|
|
3394
3620
|
ok: false,
|
|
3395
3621
|
phase: "spawn",
|
|
3396
3622
|
error: {
|
|
@@ -3405,7 +3631,7 @@ async function spawnCmd(ctx, cmd, args) {
|
|
|
3405
3631
|
}, timeoutMs);
|
|
3406
3632
|
child.on("error", (err2) => {
|
|
3407
3633
|
clearTimeout(timer);
|
|
3408
|
-
|
|
3634
|
+
resolve9({
|
|
3409
3635
|
ok: false,
|
|
3410
3636
|
phase: "spawn",
|
|
3411
3637
|
error: {
|
|
@@ -3420,14 +3646,14 @@ async function spawnCmd(ctx, cmd, args) {
|
|
|
3420
3646
|
});
|
|
3421
3647
|
child.on("close", (code) => {
|
|
3422
3648
|
clearTimeout(timer);
|
|
3423
|
-
|
|
3649
|
+
resolve9({ ok: true, exitCode: code ?? -1, stdout: stdout2, stderr });
|
|
3424
3650
|
});
|
|
3425
3651
|
});
|
|
3426
3652
|
}
|
|
3427
3653
|
|
|
3428
3654
|
// src/installers/gitCloneWithSetup.ts
|
|
3429
3655
|
function gitRevParseHead(cwd, timeoutMs = 1e4) {
|
|
3430
|
-
return new Promise((
|
|
3656
|
+
return new Promise((resolve9) => {
|
|
3431
3657
|
const isWin = process.platform === "win32";
|
|
3432
3658
|
const child = isWin ? spawn("cmd.exe", ["/c", "git", "rev-parse", "HEAD"], { cwd, windowsHide: true }) : spawn("git", ["rev-parse", "HEAD"], { cwd, shell: false });
|
|
3433
3659
|
let stdout2 = "";
|
|
@@ -3436,15 +3662,15 @@ function gitRevParseHead(cwd, timeoutMs = 1e4) {
|
|
|
3436
3662
|
});
|
|
3437
3663
|
const timer = setTimeout(() => {
|
|
3438
3664
|
child.kill("SIGKILL");
|
|
3439
|
-
|
|
3665
|
+
resolve9({ sha: "", exit: -1 });
|
|
3440
3666
|
}, timeoutMs);
|
|
3441
3667
|
child.on("error", () => {
|
|
3442
3668
|
clearTimeout(timer);
|
|
3443
|
-
|
|
3669
|
+
resolve9({ sha: "", exit: -1 });
|
|
3444
3670
|
});
|
|
3445
3671
|
child.on("close", (code) => {
|
|
3446
3672
|
clearTimeout(timer);
|
|
3447
|
-
|
|
3673
|
+
resolve9({ sha: stdout2.trim(), exit: code ?? -1 });
|
|
3448
3674
|
});
|
|
3449
3675
|
});
|
|
3450
3676
|
}
|
|
@@ -3784,7 +4010,7 @@ ${newEntry}
|
|
|
3784
4010
|
)
|
|
3785
4011
|
};
|
|
3786
4012
|
}
|
|
3787
|
-
const vr = await new Promise((
|
|
4013
|
+
const vr = await new Promise((resolve9) => {
|
|
3788
4014
|
const child = spawn(verifyShell, [verifyFlag, verifyLine], { cwd: ctx.cwd, windowsHide: true });
|
|
3789
4015
|
let stderr = "";
|
|
3790
4016
|
child.stderr?.setEncoding("utf8").on("data", (c) => {
|
|
@@ -3792,15 +4018,15 @@ ${newEntry}
|
|
|
3792
4018
|
});
|
|
3793
4019
|
const timer = setTimeout(() => {
|
|
3794
4020
|
child.kill("SIGKILL");
|
|
3795
|
-
|
|
4021
|
+
resolve9({ exitCode: -1, stderr: `${stderr}[timeout]` });
|
|
3796
4022
|
}, 15e3);
|
|
3797
4023
|
child.on("error", (e) => {
|
|
3798
4024
|
clearTimeout(timer);
|
|
3799
|
-
|
|
4025
|
+
resolve9({ exitCode: -1, stderr: e.message });
|
|
3800
4026
|
});
|
|
3801
4027
|
child.on("close", (code) => {
|
|
3802
4028
|
clearTimeout(timer);
|
|
3803
|
-
|
|
4029
|
+
resolve9({ exitCode: code ?? -1, stderr });
|
|
3804
4030
|
});
|
|
3805
4031
|
});
|
|
3806
4032
|
if (vr.exitCode !== 0) {
|
|
@@ -3932,7 +4158,7 @@ ${newEntry}
|
|
|
3932
4158
|
)
|
|
3933
4159
|
};
|
|
3934
4160
|
}
|
|
3935
|
-
const vr = await new Promise((
|
|
4161
|
+
const vr = await new Promise((resolve9) => {
|
|
3936
4162
|
const child = spawn(verifyShell, [verifyFlag, verifyLine], { cwd: ctx.cwd, windowsHide: true });
|
|
3937
4163
|
let stderr = "";
|
|
3938
4164
|
child.stderr?.setEncoding("utf8").on("data", (c) => {
|
|
@@ -3940,15 +4166,15 @@ ${newEntry}
|
|
|
3940
4166
|
});
|
|
3941
4167
|
const timer = setTimeout(() => {
|
|
3942
4168
|
child.kill("SIGKILL");
|
|
3943
|
-
|
|
4169
|
+
resolve9({ exitCode: -1, stderr: `${stderr}[timeout]` });
|
|
3944
4170
|
}, 15e3);
|
|
3945
4171
|
child.on("error", (e) => {
|
|
3946
4172
|
clearTimeout(timer);
|
|
3947
|
-
|
|
4173
|
+
resolve9({ exitCode: -1, stderr: e.message });
|
|
3948
4174
|
});
|
|
3949
4175
|
child.on("close", (code) => {
|
|
3950
4176
|
clearTimeout(timer);
|
|
3951
|
-
|
|
4177
|
+
resolve9({ exitCode: code ?? -1, stderr });
|
|
3952
4178
|
});
|
|
3953
4179
|
});
|
|
3954
4180
|
if (vr.exitCode !== 0) {
|
|
@@ -4246,7 +4472,7 @@ function formatError(e) {
|
|
|
4246
4472
|
return `${head}${where}${tip}`;
|
|
4247
4473
|
}
|
|
4248
4474
|
function registerInstall(program2) {
|
|
4249
|
-
program2.command("install <name>").description("Install an upstream (
|
|
4475
|
+
program2.command("install <name>").description("Install an upstream (immediate by default \u2014 use --dry-run for preview)").option("--apply", "(deprecated; kept for backward compat \u2014 install is immediate by default)").option("--dry-run", "preview only \u2014 do not write to disk (opt-in for advanced users)").option("--system", "allow L4 system-wide install (e.g. global npm install)").option("--non-interactive", "skip all prompts (CI / scripts) \u2014 requires --apply or --dry-run").option("--full-diff", "expand diffs longer than 200 lines").option("--no-color", "disable ANSI colors (auto-detected when piped)").option(
|
|
4250
4476
|
"--known-good",
|
|
4251
4477
|
"use known-good version lock from versions/<harnessed-ver>-known-good.yaml"
|
|
4252
4478
|
).action(async (name, raw) => {
|
|
@@ -4278,9 +4504,10 @@ function registerInstall(program2) {
|
|
|
4278
4504
|
console.error(` fix: run 'harnessed audit' to inspect manifest issues`);
|
|
4279
4505
|
process.exit(1);
|
|
4280
4506
|
}
|
|
4507
|
+
const dryRun = raw.dryRun === true;
|
|
4281
4508
|
const opts = {
|
|
4282
|
-
apply:
|
|
4283
|
-
dryRun
|
|
4509
|
+
apply: !dryRun,
|
|
4510
|
+
dryRun,
|
|
4284
4511
|
system: raw.system === true,
|
|
4285
4512
|
nonInteractive: raw.nonInteractive === true,
|
|
4286
4513
|
fullDiff: raw.fullDiff === true,
|
|
@@ -4326,11 +4553,17 @@ async function listBaseManifests(cwd) {
|
|
|
4326
4553
|
return out;
|
|
4327
4554
|
}
|
|
4328
4555
|
function registerInstallBase(program2) {
|
|
4329
|
-
program2.command("install-base").description(
|
|
4556
|
+
program2.command("install-base").description(
|
|
4557
|
+
"Install the phase 1.3 base profile (immediate by default \u2014 use --dry-run for preview)"
|
|
4558
|
+
).option(
|
|
4559
|
+
"--apply",
|
|
4560
|
+
"(deprecated; kept for backward compat \u2014 install-base is immediate by default)"
|
|
4561
|
+
).option("--dry-run", "preview only \u2014 do not write to disk (opt-in for advanced users)").option("--non-interactive", "skip all prompts (CI / scripts) \u2014 requires --apply or --dry-run").action(async (raw) => {
|
|
4330
4562
|
validateNonInteractiveFlags(raw, "install-base");
|
|
4563
|
+
const dryRun = raw.dryRun === true;
|
|
4331
4564
|
const opts = {
|
|
4332
|
-
apply:
|
|
4333
|
-
dryRun
|
|
4565
|
+
apply: !dryRun,
|
|
4566
|
+
dryRun,
|
|
4334
4567
|
system: false,
|
|
4335
4568
|
nonInteractive: raw.nonInteractive === true,
|
|
4336
4569
|
fullDiff: false,
|
|
@@ -4390,7 +4623,12 @@ function basename(upstream) {
|
|
|
4390
4623
|
return (upstream.split("/").pop() ?? upstream).replace(/\.git$/, "");
|
|
4391
4624
|
}
|
|
4392
4625
|
function registerManifestAdd(program2) {
|
|
4393
|
-
program2.command("manifest-add <upstream>").description(
|
|
4626
|
+
program2.command("manifest-add <upstream>").description(
|
|
4627
|
+
"Add a new upstream adapter (EE-5 5-question merge gate; immediate by default \u2014 use --dry-run for preview)"
|
|
4628
|
+
).option("--category <cat>", "manifest category (skill-packs | tools)", "skill-packs").option("--name <name>", "short adapter name (defaults to <upstream> basename)").option(
|
|
4629
|
+
"--apply",
|
|
4630
|
+
"(deprecated; kept for backward compat \u2014 manifest-add persists immediately by default)"
|
|
4631
|
+
).option("--dry-run", "preview only \u2014 do not write JSON (opt-in for advanced users)").option("--non-interactive", "CI/scripts \u2014 requires --apply or --dry-run; WARN-only dry-run").action(async (upstream, raw) => {
|
|
4394
4632
|
validateNonInteractiveFlags(raw, "manifest-add <upstream>");
|
|
4395
4633
|
const name = raw.name ?? basename(upstream);
|
|
4396
4634
|
const category = raw.category ?? "skill-packs";
|
|
@@ -4419,7 +4657,8 @@ function registerManifestAdd(program2) {
|
|
|
4419
4657
|
payload[f] = a;
|
|
4420
4658
|
}
|
|
4421
4659
|
rl.close();
|
|
4422
|
-
|
|
4660
|
+
const dryRun = raw.dryRun === true;
|
|
4661
|
+
if (!dryRun) {
|
|
4423
4662
|
writeFileSync(outPath, `${JSON.stringify(payload, null, 2)}
|
|
4424
4663
|
`, "utf8");
|
|
4425
4664
|
console.log(`[manifest-add] EE-5 gate passed; wrote ${outPath}`);
|
|
@@ -4433,14 +4672,19 @@ function registerManifestAdd(program2) {
|
|
|
4433
4672
|
|
|
4434
4673
|
// src/cli/research.ts
|
|
4435
4674
|
function registerResearch(program2) {
|
|
4436
|
-
program2.command("research").description(
|
|
4675
|
+
program2.command("research").description(
|
|
4676
|
+
"Run research workflow (search category sub-routing \u2192 spawn \u2192 verbatim COMPLETE; immediate by default \u2014 use --dry-run for preview)"
|
|
4677
|
+
).requiredOption("--query <text>", "research prompt (required)").option(
|
|
4678
|
+
"--apply",
|
|
4679
|
+
"(deprecated; kept for backward compat \u2014 research spawns immediately by default)"
|
|
4680
|
+
).option("--dry-run", "preview only \u2014 do not spawn subagent (opt-in for advanced users)").option("--non-interactive", "skip all prompts (CI / scripts) \u2014 requires --apply or --dry-run").option("--model <model>", "subagent model: 'haiku' | 'sonnet' | 'opus'").action(async (raw) => {
|
|
4437
4681
|
validateNonInteractiveFlags(raw, "research --query <text>");
|
|
4438
4682
|
if (!raw.query) {
|
|
4439
4683
|
console.error("error: --query <text> is required");
|
|
4440
4684
|
process.exit(2);
|
|
4441
4685
|
}
|
|
4442
4686
|
const taskCtx = { task: raw.query, task_type: "search" };
|
|
4443
|
-
if (raw.dryRun === true
|
|
4687
|
+
if (raw.dryRun === true) {
|
|
4444
4688
|
const preview = await runRouting(taskCtx, {
|
|
4445
4689
|
skillsRoot: void 0,
|
|
4446
4690
|
// Stub spawn — dry-run never reaches it; explicit COMPLETE keeps shape happy.
|
|
@@ -4458,7 +4702,9 @@ function registerResearch(program2) {
|
|
|
4458
4702
|
}
|
|
4459
4703
|
console.log(`[dry-run] matched_rule: ${preview.matchedRule?.id ?? "(fallback supervisor)"}`);
|
|
4460
4704
|
console.log(`[dry-run] query: ${raw.query}`);
|
|
4461
|
-
console.log(
|
|
4705
|
+
console.log(
|
|
4706
|
+
" (run without --dry-run to spawn the subagent and emit verbatim COMPLETE round-trip)"
|
|
4707
|
+
);
|
|
4462
4708
|
process.exit(0);
|
|
4463
4709
|
}
|
|
4464
4710
|
const result = await runRouting(taskCtx, {
|
|
@@ -4562,6 +4808,82 @@ function registerRollback(program2) {
|
|
|
4562
4808
|
});
|
|
4563
4809
|
}
|
|
4564
4810
|
init_checkAgentTeams();
|
|
4811
|
+
var FLAT_LEGACY_DEPRECATED = /* @__PURE__ */ new Set(["plan-feature", "execute-task", "verify-work"]);
|
|
4812
|
+
var FLAT_LEGACY_KEEP = /* @__PURE__ */ new Set(["research", "retro"]);
|
|
4813
|
+
var NON_WORKFLOW_DIRS = /* @__PURE__ */ new Set(["disciplines", "judgments"]);
|
|
4814
|
+
async function scanWorkflowsNested(workflowsDir, entries) {
|
|
4815
|
+
const workflows = [];
|
|
4816
|
+
const deprecated = [];
|
|
4817
|
+
for (const entry of entries.sort()) {
|
|
4818
|
+
if (NON_WORKFLOW_DIRS.has(entry)) continue;
|
|
4819
|
+
const src = join(workflowsDir, entry);
|
|
4820
|
+
let s;
|
|
4821
|
+
try {
|
|
4822
|
+
s = await stat(src);
|
|
4823
|
+
} catch {
|
|
4824
|
+
continue;
|
|
4825
|
+
}
|
|
4826
|
+
if (!s.isDirectory()) continue;
|
|
4827
|
+
let hasFlatSkill = false;
|
|
4828
|
+
try {
|
|
4829
|
+
await stat(join(src, "SKILL.md"));
|
|
4830
|
+
hasFlatSkill = true;
|
|
4831
|
+
} catch {
|
|
4832
|
+
hasFlatSkill = false;
|
|
4833
|
+
}
|
|
4834
|
+
if (hasFlatSkill) {
|
|
4835
|
+
if (FLAT_LEGACY_DEPRECATED.has(entry)) {
|
|
4836
|
+
deprecated.push(entry);
|
|
4837
|
+
continue;
|
|
4838
|
+
}
|
|
4839
|
+
if (FLAT_LEGACY_KEEP.has(entry)) {
|
|
4840
|
+
workflows.push({ name: entry, relPath: entry, isMaster: false });
|
|
4841
|
+
continue;
|
|
4842
|
+
}
|
|
4843
|
+
workflows.push({ name: entry, relPath: entry, isMaster: false });
|
|
4844
|
+
continue;
|
|
4845
|
+
}
|
|
4846
|
+
let subEntries;
|
|
4847
|
+
try {
|
|
4848
|
+
subEntries = await readdir(src);
|
|
4849
|
+
} catch {
|
|
4850
|
+
continue;
|
|
4851
|
+
}
|
|
4852
|
+
for (const sub of subEntries.sort()) {
|
|
4853
|
+
const subDir = join(src, sub);
|
|
4854
|
+
let ss;
|
|
4855
|
+
try {
|
|
4856
|
+
ss = await stat(subDir);
|
|
4857
|
+
} catch {
|
|
4858
|
+
continue;
|
|
4859
|
+
}
|
|
4860
|
+
if (!ss.isDirectory()) continue;
|
|
4861
|
+
try {
|
|
4862
|
+
await stat(join(subDir, "SKILL.md"));
|
|
4863
|
+
} catch {
|
|
4864
|
+
continue;
|
|
4865
|
+
}
|
|
4866
|
+
const name = sub === "auto" ? entry : `${entry}-${sub}`;
|
|
4867
|
+
workflows.push({ name, relPath: `${entry}/${sub}`, isMaster: sub === "auto" });
|
|
4868
|
+
}
|
|
4869
|
+
}
|
|
4870
|
+
return { workflows, deprecated };
|
|
4871
|
+
}
|
|
4872
|
+
function renderDeprecationBlock(deprecated) {
|
|
4873
|
+
if (deprecated.length === 0) return "";
|
|
4874
|
+
return [
|
|
4875
|
+
"\u26A0\uFE0F v3.0 BREAKING \u2014 v2 legacy slash cmd deprecated:",
|
|
4876
|
+
" /plan-feature \u2192 /plan (master) | /plan-phase (sub)",
|
|
4877
|
+
" /execute-task \u2192 /task (master) | /task-{clarify,code,test,deliver} (sub)",
|
|
4878
|
+
" /verify-work \u2192 /verify (master) | /verify-{progress,paranoid,qa,security,design,simplify,multispec} (sub)",
|
|
4879
|
+
" /research, /retro \u4E0D\u53D8",
|
|
4880
|
+
" \u8BE6\u89C1 CHANGELOG [3.0.0]",
|
|
4881
|
+
` skipped install: ${deprecated.sort().join(", ")}`,
|
|
4882
|
+
""
|
|
4883
|
+
].join("\n");
|
|
4884
|
+
}
|
|
4885
|
+
|
|
4886
|
+
// src/cli/lib/setup-helpers.ts
|
|
4565
4887
|
var PHASE_212 = /* @__PURE__ */ new Set([
|
|
4566
4888
|
"cc-plugin-marketplace",
|
|
4567
4889
|
"git-clone-with-setup",
|
|
@@ -4581,18 +4903,7 @@ async function warnIfAgentTeamsMissing() {
|
|
|
4581
4903
|
);
|
|
4582
4904
|
}
|
|
4583
4905
|
async function scanWorkflowsWithSkill(workflowsDir, entries) {
|
|
4584
|
-
|
|
4585
|
-
for (const entry of entries.sort()) {
|
|
4586
|
-
const src = join(workflowsDir, entry);
|
|
4587
|
-
try {
|
|
4588
|
-
const s = await stat(src);
|
|
4589
|
-
if (!s.isDirectory()) continue;
|
|
4590
|
-
await stat(join(src, "SKILL.md"));
|
|
4591
|
-
out.push(entry);
|
|
4592
|
-
} catch {
|
|
4593
|
-
}
|
|
4594
|
-
}
|
|
4595
|
-
return out;
|
|
4906
|
+
return scanWorkflowsNested(workflowsDir, entries);
|
|
4596
4907
|
}
|
|
4597
4908
|
async function runStepBInstall(manifestPaths) {
|
|
4598
4909
|
const opts = {
|
|
@@ -4678,7 +4989,12 @@ function registerSetup(program2) {
|
|
|
4678
4989
|
console.error(`error: workflows directory not found at ${workflowsDir}`);
|
|
4679
4990
|
process.exit(1);
|
|
4680
4991
|
}
|
|
4681
|
-
const toInstall = await scanWorkflowsWithSkill(
|
|
4992
|
+
const { workflows: toInstall, deprecated } = await scanWorkflowsWithSkill(
|
|
4993
|
+
workflowsDir,
|
|
4994
|
+
entries
|
|
4995
|
+
);
|
|
4996
|
+
const depBlock = renderDeprecationBlock(deprecated);
|
|
4997
|
+
if (depBlock) console.log(depBlock);
|
|
4682
4998
|
if (toInstall.length === 0) {
|
|
4683
4999
|
console.log("setup: no workflow directories with SKILL.md found \u2014 nothing to install");
|
|
4684
5000
|
process.exit(2);
|
|
@@ -4687,22 +5003,24 @@ function registerSetup(program2) {
|
|
|
4687
5003
|
console.log(
|
|
4688
5004
|
`[dry-run] setup would install ${toInstall.length} workflow(s) to ${skillsBase}:`
|
|
4689
5005
|
);
|
|
4690
|
-
for (const
|
|
4691
|
-
|
|
5006
|
+
for (const wf of toInstall) {
|
|
5007
|
+
const masterTag = wf.isMaster ? " (master)" : "";
|
|
5008
|
+
console.log(` ${wf.name} \u2192 ${join(skillsBase, wf.name)}${masterTag}`);
|
|
4692
5009
|
}
|
|
4693
5010
|
console.log(` run without --dry-run to execute`);
|
|
4694
5011
|
process.exit(0);
|
|
4695
5012
|
}
|
|
4696
5013
|
let skillsInstalled = 0;
|
|
4697
|
-
for (const
|
|
4698
|
-
const src = join(workflowsDir,
|
|
4699
|
-
const dst = join(skillsBase, name);
|
|
5014
|
+
for (const wf of toInstall) {
|
|
5015
|
+
const src = join(workflowsDir, wf.relPath);
|
|
5016
|
+
const dst = join(skillsBase, wf.name);
|
|
4700
5017
|
try {
|
|
4701
5018
|
await cp(src, dst, { recursive: true, force: true });
|
|
4702
|
-
|
|
5019
|
+
const masterTag = wf.isMaster ? " (master)" : "";
|
|
5020
|
+
console.log(` [A] installed ${wf.name} \u2192 ${dst}${masterTag}`);
|
|
4703
5021
|
skillsInstalled++;
|
|
4704
5022
|
} catch (e) {
|
|
4705
|
-
console.error(` error: failed to copy ${name}: ${e.message}`);
|
|
5023
|
+
console.error(` error: failed to copy ${wf.name}: ${e.message}`);
|
|
4706
5024
|
process.exit(1);
|
|
4707
5025
|
}
|
|
4708
5026
|
}
|
|
@@ -4963,7 +5281,7 @@ var uninstallNpmCli = async (ctx) => {
|
|
|
4963
5281
|
const m = install.cmd.match(/npm\s+(?:install|i)\s+(?:-g\s+)?(\S+)/);
|
|
4964
5282
|
const pkg = m?.[1] ?? ctx.manifest.metadata.upstream.source;
|
|
4965
5283
|
const isWin = process.platform === "win32";
|
|
4966
|
-
const result = await new Promise((
|
|
5284
|
+
const result = await new Promise((resolve9) => {
|
|
4967
5285
|
const child = isWin ? spawn("cmd.exe", ["/c", "npm", "uninstall", "-g", pkg], { windowsHide: true }) : spawn("npm", ["uninstall", "-g", pkg], { shell: false });
|
|
4968
5286
|
let stderr = "";
|
|
4969
5287
|
child.stderr?.setEncoding("utf8").on("data", (c) => {
|
|
@@ -4971,15 +5289,15 @@ var uninstallNpmCli = async (ctx) => {
|
|
|
4971
5289
|
});
|
|
4972
5290
|
const timer = setTimeout(() => {
|
|
4973
5291
|
child.kill("SIGKILL");
|
|
4974
|
-
|
|
5292
|
+
resolve9({ exitCode: -1, stderr: `${stderr}[timeout]` });
|
|
4975
5293
|
}, 3e4);
|
|
4976
5294
|
child.on("error", (e) => {
|
|
4977
5295
|
clearTimeout(timer);
|
|
4978
|
-
|
|
5296
|
+
resolve9({ exitCode: -1, stderr: e.message });
|
|
4979
5297
|
});
|
|
4980
5298
|
child.on("close", (code) => {
|
|
4981
5299
|
clearTimeout(timer);
|
|
4982
|
-
|
|
5300
|
+
resolve9({ exitCode: code ?? -1, stderr });
|
|
4983
5301
|
});
|
|
4984
5302
|
});
|
|
4985
5303
|
if (result.exitCode !== 0) {
|
|
@@ -5029,12 +5347,12 @@ async function runUninstall(manifest, opts) {
|
|
|
5029
5347
|
|
|
5030
5348
|
// src/cli/uninstall.ts
|
|
5031
5349
|
function registerUninstall(program2) {
|
|
5032
|
-
program2.command("uninstall <name>").description("Uninstall an upstream (
|
|
5350
|
+
program2.command("uninstall <name>").description("Uninstall an upstream (immediate by default \u2014 use --dry-run for preview)").option("--apply", "(deprecated; kept for backward compat \u2014 uninstall is immediate by default)").option("--dry-run", "preview only \u2014 do not delete files (opt-in for advanced users)").option("--yes", "skip interactive confirm (CI / scripts) \u2014 fatal with --dry-run").option("--non-interactive", "alias for --yes (CI compat)").action(async (name, raw) => {
|
|
5033
5351
|
const yes = raw.yes === true || raw.nonInteractive === true;
|
|
5034
|
-
if (yes &&
|
|
5352
|
+
if (yes && raw.dryRun) {
|
|
5035
5353
|
console.error(
|
|
5036
|
-
`error: --yes
|
|
5037
|
-
fix: harnessed uninstall ${name} --yes --
|
|
5354
|
+
`error: --yes is incompatible with --dry-run (dry-run does not mutate)
|
|
5355
|
+
fix: harnessed uninstall ${name} --yes (immediate) OR harnessed uninstall ${name} --dry-run (preview)`
|
|
5038
5356
|
);
|
|
5039
5357
|
process.exit(2);
|
|
5040
5358
|
}
|
|
@@ -5065,10 +5383,10 @@ function registerUninstall(program2) {
|
|
|
5065
5383
|
process.exit(1);
|
|
5066
5384
|
}
|
|
5067
5385
|
const method = v.manifest.spec.install.method;
|
|
5068
|
-
const dryRun = raw.dryRun === true
|
|
5386
|
+
const dryRun = raw.dryRun === true;
|
|
5069
5387
|
if (dryRun) {
|
|
5070
5388
|
console.log(`[dry-run] would uninstall '${resolvedName}' via method '${method}'`);
|
|
5071
|
-
console.log(` run
|
|
5389
|
+
console.log(` run without --dry-run to execute`);
|
|
5072
5390
|
process.exit(2);
|
|
5073
5391
|
}
|
|
5074
5392
|
if (!yes) {
|