harnessed 2.0.0 → 3.0.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/README.md +263 -28
- package/dist/cli.mjs +486 -190
- 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.0"};
|
|
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
|
});
|
|
@@ -1638,19 +1646,132 @@ function registerAuditLog(program2) {
|
|
|
1638
1646
|
}
|
|
1639
1647
|
);
|
|
1640
1648
|
}
|
|
1649
|
+
function getBackupRoot() {
|
|
1650
|
+
return join(homedir(), ".harnessed", "backups");
|
|
1651
|
+
}
|
|
1652
|
+
var HOME_DIR = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
1653
|
+
function mirrorPath(target, scope, backupDir) {
|
|
1654
|
+
const root = scope === "HOME" ? HOME_DIR : ".";
|
|
1655
|
+
const rel = root ? relative(root, target) : target;
|
|
1656
|
+
if (!rel || rel.startsWith("..")) {
|
|
1657
|
+
const flat = createHash("sha1").update(target).digest("hex").slice(0, 16);
|
|
1658
|
+
return join(backupDir, scope, flat);
|
|
1659
|
+
}
|
|
1660
|
+
return join(backupDir, scope, rel);
|
|
1661
|
+
}
|
|
1662
|
+
function detectEol(buf) {
|
|
1663
|
+
return buf.includes("\r\n") ? "crlf" : "lf";
|
|
1664
|
+
}
|
|
1665
|
+
async function backup(plan, ctx) {
|
|
1666
|
+
const filename = ctx.manifest.metadata.name;
|
|
1667
|
+
const backupId = (/* @__PURE__ */ new Date()).toISOString().replace(/:/g, "-");
|
|
1668
|
+
const backupDir = join(getBackupRoot(), backupId);
|
|
1669
|
+
try {
|
|
1670
|
+
await mkdir(backupDir, { recursive: true });
|
|
1671
|
+
} catch (err2) {
|
|
1672
|
+
return {
|
|
1673
|
+
ok: false,
|
|
1674
|
+
error: {
|
|
1675
|
+
file: filename,
|
|
1676
|
+
path: "/",
|
|
1677
|
+
message: `failed to create backup dir ${backupDir}: ${err2.message}`,
|
|
1678
|
+
line: null,
|
|
1679
|
+
column: null,
|
|
1680
|
+
keyword: "backup-mkdir-failed"
|
|
1681
|
+
}
|
|
1682
|
+
};
|
|
1683
|
+
}
|
|
1684
|
+
const entries = [];
|
|
1685
|
+
for (const file of plan.files) {
|
|
1686
|
+
let buf;
|
|
1687
|
+
try {
|
|
1688
|
+
buf = await readFile(file.target);
|
|
1689
|
+
} catch (err2) {
|
|
1690
|
+
const code = err2.code;
|
|
1691
|
+
if (code === "ENOENT" && file.oldText === "") {
|
|
1692
|
+
entries.push({
|
|
1693
|
+
target: file.target,
|
|
1694
|
+
backup: "",
|
|
1695
|
+
// sentinel: no backup written; rollback should unlink target
|
|
1696
|
+
sha1: "",
|
|
1697
|
+
eol: "lf"
|
|
1698
|
+
// moot for non-existent file; default to lf
|
|
1699
|
+
});
|
|
1700
|
+
continue;
|
|
1701
|
+
}
|
|
1702
|
+
return {
|
|
1703
|
+
ok: false,
|
|
1704
|
+
error: {
|
|
1705
|
+
file: filename,
|
|
1706
|
+
path: file.target,
|
|
1707
|
+
message: `failed to read original file for backup: ${err2.message}`,
|
|
1708
|
+
line: null,
|
|
1709
|
+
column: null,
|
|
1710
|
+
keyword: "backup-read-failed"
|
|
1711
|
+
}
|
|
1712
|
+
};
|
|
1713
|
+
}
|
|
1714
|
+
const sha1 = createHash("sha1").update(buf).digest("hex");
|
|
1715
|
+
const eol = detectEol(buf);
|
|
1716
|
+
const dest = mirrorPath(file.target, file.scope, backupDir);
|
|
1717
|
+
try {
|
|
1718
|
+
await mkdir(dirname(dest), { recursive: true });
|
|
1719
|
+
await writeFile(dest, buf);
|
|
1720
|
+
} catch (err2) {
|
|
1721
|
+
return {
|
|
1722
|
+
ok: false,
|
|
1723
|
+
error: {
|
|
1724
|
+
file: filename,
|
|
1725
|
+
path: dest,
|
|
1726
|
+
message: `failed to write backup copy: ${err2.message}`,
|
|
1727
|
+
line: null,
|
|
1728
|
+
column: null,
|
|
1729
|
+
keyword: "backup-write-failed"
|
|
1730
|
+
}
|
|
1731
|
+
};
|
|
1732
|
+
}
|
|
1733
|
+
entries.push({ target: file.target, backup: dest, sha1, eol });
|
|
1734
|
+
}
|
|
1735
|
+
const metadata = {
|
|
1736
|
+
installer: filename,
|
|
1737
|
+
manifest: filename,
|
|
1738
|
+
timestamp: backupId,
|
|
1739
|
+
files: entries
|
|
1740
|
+
};
|
|
1741
|
+
const metadataPath = join(backupDir, "metadata.json");
|
|
1742
|
+
try {
|
|
1743
|
+
await writeFile(metadataPath, `${JSON.stringify(metadata, null, 2)}
|
|
1744
|
+
`, "utf8");
|
|
1745
|
+
} catch (err2) {
|
|
1746
|
+
return {
|
|
1747
|
+
ok: false,
|
|
1748
|
+
error: {
|
|
1749
|
+
file: filename,
|
|
1750
|
+
path: metadataPath,
|
|
1751
|
+
message: `failed to write metadata.json: ${err2.message}`,
|
|
1752
|
+
line: null,
|
|
1753
|
+
column: null,
|
|
1754
|
+
keyword: "backup-metadata-failed"
|
|
1755
|
+
}
|
|
1756
|
+
};
|
|
1757
|
+
}
|
|
1758
|
+
return { ok: true, backupId, backupDir };
|
|
1759
|
+
}
|
|
1760
|
+
|
|
1761
|
+
// src/cli/backup-list.ts
|
|
1641
1762
|
function registerBackupList(program2) {
|
|
1642
1763
|
const backup2 = program2.command("backup").description("Backup snapshot operations");
|
|
1643
1764
|
backup2.command("list").description("List backup snapshots under .harnessed-backup/").action(async () => {
|
|
1644
|
-
const root =
|
|
1765
|
+
const root = getBackupRoot();
|
|
1645
1766
|
let dirs;
|
|
1646
1767
|
try {
|
|
1647
1768
|
dirs = (await readdir(root, { withFileTypes: true })).filter((e) => e.isDirectory()).map((e) => e.name).sort();
|
|
1648
1769
|
} catch {
|
|
1649
|
-
console.log(
|
|
1770
|
+
console.log(`no backups found (${root} absent)`);
|
|
1650
1771
|
return;
|
|
1651
1772
|
}
|
|
1652
1773
|
if (dirs.length === 0) {
|
|
1653
|
-
console.log(
|
|
1774
|
+
console.log(`no backups found (${root} empty)`);
|
|
1654
1775
|
return;
|
|
1655
1776
|
}
|
|
1656
1777
|
for (const ts of dirs) {
|
|
@@ -1819,6 +1940,107 @@ function registerDoctor(program2) {
|
|
|
1819
1940
|
});
|
|
1820
1941
|
}
|
|
1821
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
|
+
|
|
1822
2044
|
// src/routing/completionSchema.ts
|
|
1823
2045
|
var COMPLETION_SCHEMA = {
|
|
1824
2046
|
type: "object",
|
|
@@ -2523,15 +2745,100 @@ var PhasesSchema = Type.Object(
|
|
|
2523
2745
|
|
|
2524
2746
|
// src/workflow/schema/workflow.ts
|
|
2525
2747
|
init_schemaVersion();
|
|
2748
|
+
|
|
2749
|
+
// src/workflow/schema/workflow.v2.ts
|
|
2750
|
+
init_schemaVersion();
|
|
2526
2751
|
var ModelTier2 = Type.Union([Type.Literal("haiku"), Type.Literal("sonnet"), Type.Literal("opus")]);
|
|
2527
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
|
+
]);
|
|
2528
2814
|
var OnClause = Type.Object(
|
|
2529
2815
|
{
|
|
2530
2816
|
if: Type.String(),
|
|
2531
2817
|
// expr-eval expression OR judgments.<file>.<gate>.fires ref
|
|
2532
2818
|
invoke: Type.Optional(Type.String()),
|
|
2533
2819
|
// '{{ capabilities.<name>.cmd }}' OR literal
|
|
2534
|
-
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)
|
|
2535
2842
|
},
|
|
2536
2843
|
{ additionalProperties: false }
|
|
2537
2844
|
);
|
|
@@ -2550,14 +2857,14 @@ var PhaseFallback = Type.Object(
|
|
|
2550
2857
|
},
|
|
2551
2858
|
{ additionalProperties: false }
|
|
2552
2859
|
);
|
|
2553
|
-
var
|
|
2860
|
+
var WorkflowPhaseV3 = Type.Object(
|
|
2554
2861
|
{
|
|
2555
2862
|
id: Type.String({ minLength: 1 }),
|
|
2556
2863
|
name: Type.Optional(Type.String()),
|
|
2557
2864
|
upstream: Type.Optional(Type.String()),
|
|
2558
2865
|
capability: Type.Optional(Type.String()),
|
|
2559
2866
|
// '{{ capabilities.ralph-loop.cmd }}'
|
|
2560
|
-
model: Type.Optional(
|
|
2867
|
+
model: Type.Optional(ModelTier3),
|
|
2561
2868
|
invokes: Type.Optional(Type.String()),
|
|
2562
2869
|
// legacy slash-cmd OR JINJA template
|
|
2563
2870
|
args: Type.Optional(Type.Record(Type.String(), Type.Unknown())),
|
|
@@ -2571,16 +2878,25 @@ var WorkflowPhaseV2 = Type.Object(
|
|
|
2571
2878
|
Type.Union([Type.Number(), Type.String()])
|
|
2572
2879
|
// numeric literal OR jinja '{{ defaults.x.y }}'
|
|
2573
2880
|
),
|
|
2574
|
-
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
|
|
2575
2884
|
},
|
|
2576
2885
|
{ additionalProperties: false }
|
|
2577
2886
|
);
|
|
2578
|
-
|
|
2887
|
+
Type.Object(
|
|
2579
2888
|
{
|
|
2580
|
-
schema_version: Type.Literal(SCHEMA_VERSIONS.
|
|
2889
|
+
schema_version: Type.Literal(SCHEMA_VERSIONS.workflow_v3),
|
|
2581
2890
|
workflow: Type.String({ minLength: 1 }),
|
|
2582
2891
|
description: Type.Optional(Type.String()),
|
|
2583
|
-
|
|
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 必有
|
|
2584
2900
|
},
|
|
2585
2901
|
{ additionalProperties: false }
|
|
2586
2902
|
);
|
|
@@ -2663,6 +2979,22 @@ function registerExecuteTask(program2) {
|
|
|
2663
2979
|
break;
|
|
2664
2980
|
}
|
|
2665
2981
|
}
|
|
2982
|
+
try {
|
|
2983
|
+
const stagedOut = execSync("git status --porcelain", { encoding: "utf8" });
|
|
2984
|
+
const changedFiles = stagedOut.split("\n").filter((l) => l.trim().length > 0).map((l) => l.slice(3).trim());
|
|
2985
|
+
await runBeforeCommitHook({
|
|
2986
|
+
changedFiles,
|
|
2987
|
+
cmdArgs: [],
|
|
2988
|
+
packageRoot: process.cwd(),
|
|
2989
|
+
cmdType: "git-commit",
|
|
2990
|
+
hasUserApproval: true
|
|
2991
|
+
// --apply explicit
|
|
2992
|
+
});
|
|
2993
|
+
} catch (err2) {
|
|
2994
|
+
console.warn(
|
|
2995
|
+
`\u26A0\uFE0F before-commit pre-flight skipped (${err2.message}); subagent will biome-check at commit time.`
|
|
2996
|
+
);
|
|
2997
|
+
}
|
|
2666
2998
|
const result = await runRouting(taskCtx, {
|
|
2667
2999
|
maxIterations: raw.maxIterations ?? 20,
|
|
2668
3000
|
...raw.model ? { agentOpts: { modelOverride: raw.model } } : {},
|
|
@@ -2721,12 +3053,12 @@ function registerGc(program2) {
|
|
|
2721
3053
|
return;
|
|
2722
3054
|
}
|
|
2723
3055
|
const keepLast = Number.parseInt(opts.keepLast ?? "0", 10);
|
|
2724
|
-
const root =
|
|
3056
|
+
const root = getBackupRoot();
|
|
2725
3057
|
let dirs;
|
|
2726
3058
|
try {
|
|
2727
3059
|
dirs = (await readdir(root, { withFileTypes: true })).filter((e) => e.isDirectory()).map((e) => e.name).sort();
|
|
2728
3060
|
} catch {
|
|
2729
|
-
console.log(
|
|
3061
|
+
console.log(`no backups found (${root} absent) \u2014 nothing to gc`);
|
|
2730
3062
|
return;
|
|
2731
3063
|
}
|
|
2732
3064
|
const cutoff = Date.now() - olderMs;
|
|
@@ -2763,114 +3095,6 @@ function registerGc(program2) {
|
|
|
2763
3095
|
if (dryRun) console.log("\n(dry-run \u2014 re-run with --apply to actually delete)");
|
|
2764
3096
|
});
|
|
2765
3097
|
}
|
|
2766
|
-
var HOME_DIR = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
2767
|
-
function mirrorPath(target, scope, backupDir) {
|
|
2768
|
-
const root = scope === "HOME" ? HOME_DIR : ".";
|
|
2769
|
-
const rel = root ? relative(root, target) : target;
|
|
2770
|
-
if (!rel || rel.startsWith("..")) {
|
|
2771
|
-
const flat = createHash("sha1").update(target).digest("hex").slice(0, 16);
|
|
2772
|
-
return join(backupDir, scope, flat);
|
|
2773
|
-
}
|
|
2774
|
-
return join(backupDir, scope, rel);
|
|
2775
|
-
}
|
|
2776
|
-
function detectEol(buf) {
|
|
2777
|
-
return buf.includes("\r\n") ? "crlf" : "lf";
|
|
2778
|
-
}
|
|
2779
|
-
async function backup(plan, ctx) {
|
|
2780
|
-
const filename = ctx.manifest.metadata.name;
|
|
2781
|
-
const backupId = (/* @__PURE__ */ new Date()).toISOString().replace(/:/g, "-");
|
|
2782
|
-
const backupDir = join(ctx.cwd, ".harnessed-backup", backupId);
|
|
2783
|
-
try {
|
|
2784
|
-
await mkdir(backupDir, { recursive: true });
|
|
2785
|
-
} catch (err2) {
|
|
2786
|
-
return {
|
|
2787
|
-
ok: false,
|
|
2788
|
-
error: {
|
|
2789
|
-
file: filename,
|
|
2790
|
-
path: "/",
|
|
2791
|
-
message: `failed to create backup dir ${backupDir}: ${err2.message}`,
|
|
2792
|
-
line: null,
|
|
2793
|
-
column: null,
|
|
2794
|
-
keyword: "backup-mkdir-failed"
|
|
2795
|
-
}
|
|
2796
|
-
};
|
|
2797
|
-
}
|
|
2798
|
-
const entries = [];
|
|
2799
|
-
for (const file of plan.files) {
|
|
2800
|
-
let buf;
|
|
2801
|
-
try {
|
|
2802
|
-
buf = await readFile(file.target);
|
|
2803
|
-
} catch (err2) {
|
|
2804
|
-
const code = err2.code;
|
|
2805
|
-
if (code === "ENOENT" && file.oldText === "") {
|
|
2806
|
-
entries.push({
|
|
2807
|
-
target: file.target,
|
|
2808
|
-
backup: "",
|
|
2809
|
-
// sentinel: no backup written; rollback should unlink target
|
|
2810
|
-
sha1: "",
|
|
2811
|
-
eol: "lf"
|
|
2812
|
-
// moot for non-existent file; default to lf
|
|
2813
|
-
});
|
|
2814
|
-
continue;
|
|
2815
|
-
}
|
|
2816
|
-
return {
|
|
2817
|
-
ok: false,
|
|
2818
|
-
error: {
|
|
2819
|
-
file: filename,
|
|
2820
|
-
path: file.target,
|
|
2821
|
-
message: `failed to read original file for backup: ${err2.message}`,
|
|
2822
|
-
line: null,
|
|
2823
|
-
column: null,
|
|
2824
|
-
keyword: "backup-read-failed"
|
|
2825
|
-
}
|
|
2826
|
-
};
|
|
2827
|
-
}
|
|
2828
|
-
const sha1 = createHash("sha1").update(buf).digest("hex");
|
|
2829
|
-
const eol = detectEol(buf);
|
|
2830
|
-
const dest = mirrorPath(file.target, file.scope, backupDir);
|
|
2831
|
-
try {
|
|
2832
|
-
await mkdir(dirname(dest), { recursive: true });
|
|
2833
|
-
await writeFile(dest, buf);
|
|
2834
|
-
} catch (err2) {
|
|
2835
|
-
return {
|
|
2836
|
-
ok: false,
|
|
2837
|
-
error: {
|
|
2838
|
-
file: filename,
|
|
2839
|
-
path: dest,
|
|
2840
|
-
message: `failed to write backup copy: ${err2.message}`,
|
|
2841
|
-
line: null,
|
|
2842
|
-
column: null,
|
|
2843
|
-
keyword: "backup-write-failed"
|
|
2844
|
-
}
|
|
2845
|
-
};
|
|
2846
|
-
}
|
|
2847
|
-
entries.push({ target: file.target, backup: dest, sha1, eol });
|
|
2848
|
-
}
|
|
2849
|
-
const metadata = {
|
|
2850
|
-
installer: filename,
|
|
2851
|
-
manifest: filename,
|
|
2852
|
-
timestamp: backupId,
|
|
2853
|
-
files: entries
|
|
2854
|
-
};
|
|
2855
|
-
const metadataPath = join(backupDir, "metadata.json");
|
|
2856
|
-
try {
|
|
2857
|
-
await writeFile(metadataPath, `${JSON.stringify(metadata, null, 2)}
|
|
2858
|
-
`, "utf8");
|
|
2859
|
-
} catch (err2) {
|
|
2860
|
-
return {
|
|
2861
|
-
ok: false,
|
|
2862
|
-
error: {
|
|
2863
|
-
file: filename,
|
|
2864
|
-
path: metadataPath,
|
|
2865
|
-
message: `failed to write metadata.json: ${err2.message}`,
|
|
2866
|
-
line: null,
|
|
2867
|
-
column: null,
|
|
2868
|
-
keyword: "backup-metadata-failed"
|
|
2869
|
-
}
|
|
2870
|
-
};
|
|
2871
|
-
}
|
|
2872
|
-
return { ok: true, backupId, backupDir };
|
|
2873
|
-
}
|
|
2874
3098
|
async function confirmAt(level, ctx) {
|
|
2875
3099
|
if (level === "L4" && !ctx.opts.system) {
|
|
2876
3100
|
if (!ctx.opts.nonInteractive) {
|
|
@@ -3152,7 +3376,7 @@ var installCcHookAdd = async (ctx) => {
|
|
|
3152
3376
|
return { ok: true, backupId: bk.backupId, appliedFiles: [settingsPath] };
|
|
3153
3377
|
};
|
|
3154
3378
|
function runArgs(claudeArgs, cwd, timeoutMs = 15e3) {
|
|
3155
|
-
return new Promise((
|
|
3379
|
+
return new Promise((resolve9) => {
|
|
3156
3380
|
const isWin = process.platform === "win32";
|
|
3157
3381
|
const child = isWin ? spawn("cmd.exe", ["/c", "claude", ...claudeArgs], { cwd, windowsHide: true }) : spawn("claude", claudeArgs, { cwd, shell: false });
|
|
3158
3382
|
let stderr = "";
|
|
@@ -3161,15 +3385,15 @@ function runArgs(claudeArgs, cwd, timeoutMs = 15e3) {
|
|
|
3161
3385
|
});
|
|
3162
3386
|
const timer = setTimeout(() => {
|
|
3163
3387
|
child.kill("SIGKILL");
|
|
3164
|
-
|
|
3388
|
+
resolve9({ exitCode: -1, stderr: `${stderr}[timeout after ${timeoutMs}ms]` });
|
|
3165
3389
|
}, timeoutMs);
|
|
3166
3390
|
child.on("error", (e) => {
|
|
3167
3391
|
clearTimeout(timer);
|
|
3168
|
-
|
|
3392
|
+
resolve9({ exitCode: -1, stderr: `${stderr}${e.message}` });
|
|
3169
3393
|
});
|
|
3170
3394
|
child.on("close", (code) => {
|
|
3171
3395
|
clearTimeout(timer);
|
|
3172
|
-
|
|
3396
|
+
resolve9({ exitCode: code ?? -1, stderr });
|
|
3173
3397
|
});
|
|
3174
3398
|
});
|
|
3175
3399
|
}
|
|
@@ -3310,7 +3534,7 @@ ${newEntry}
|
|
|
3310
3534
|
)
|
|
3311
3535
|
};
|
|
3312
3536
|
}
|
|
3313
|
-
const vr = await new Promise((
|
|
3537
|
+
const vr = await new Promise((resolve9) => {
|
|
3314
3538
|
const child = spawn(verifyShell, [verifyFlag, verifyLine], { cwd: ctx.cwd, windowsHide: true });
|
|
3315
3539
|
let stderr = "";
|
|
3316
3540
|
child.stderr?.setEncoding("utf8").on("data", (c) => {
|
|
@@ -3318,15 +3542,15 @@ ${newEntry}
|
|
|
3318
3542
|
});
|
|
3319
3543
|
const timer = setTimeout(() => {
|
|
3320
3544
|
child.kill("SIGKILL");
|
|
3321
|
-
|
|
3545
|
+
resolve9({ exitCode: -1, stderr: `${stderr}[timeout]` });
|
|
3322
3546
|
}, 15e3);
|
|
3323
3547
|
child.on("error", (e) => {
|
|
3324
3548
|
clearTimeout(timer);
|
|
3325
|
-
|
|
3549
|
+
resolve9({ exitCode: -1, stderr: e.message });
|
|
3326
3550
|
});
|
|
3327
3551
|
child.on("close", (code) => {
|
|
3328
3552
|
clearTimeout(timer);
|
|
3329
|
-
|
|
3553
|
+
resolve9({ exitCode: code ?? -1, stderr });
|
|
3330
3554
|
});
|
|
3331
3555
|
});
|
|
3332
3556
|
if (vr.exitCode !== 0) {
|
|
@@ -3382,10 +3606,10 @@ async function spawnCmd(ctx, cmd, args) {
|
|
|
3382
3606
|
child.stderr?.setEncoding("utf8").on("data", (chunk) => {
|
|
3383
3607
|
stderr += chunk;
|
|
3384
3608
|
});
|
|
3385
|
-
return await new Promise((
|
|
3609
|
+
return await new Promise((resolve9) => {
|
|
3386
3610
|
const timer = setTimeout(() => {
|
|
3387
3611
|
child.kill("SIGKILL");
|
|
3388
|
-
|
|
3612
|
+
resolve9({
|
|
3389
3613
|
ok: false,
|
|
3390
3614
|
phase: "spawn",
|
|
3391
3615
|
error: {
|
|
@@ -3400,7 +3624,7 @@ async function spawnCmd(ctx, cmd, args) {
|
|
|
3400
3624
|
}, timeoutMs);
|
|
3401
3625
|
child.on("error", (err2) => {
|
|
3402
3626
|
clearTimeout(timer);
|
|
3403
|
-
|
|
3627
|
+
resolve9({
|
|
3404
3628
|
ok: false,
|
|
3405
3629
|
phase: "spawn",
|
|
3406
3630
|
error: {
|
|
@@ -3415,14 +3639,14 @@ async function spawnCmd(ctx, cmd, args) {
|
|
|
3415
3639
|
});
|
|
3416
3640
|
child.on("close", (code) => {
|
|
3417
3641
|
clearTimeout(timer);
|
|
3418
|
-
|
|
3642
|
+
resolve9({ ok: true, exitCode: code ?? -1, stdout: stdout2, stderr });
|
|
3419
3643
|
});
|
|
3420
3644
|
});
|
|
3421
3645
|
}
|
|
3422
3646
|
|
|
3423
3647
|
// src/installers/gitCloneWithSetup.ts
|
|
3424
3648
|
function gitRevParseHead(cwd, timeoutMs = 1e4) {
|
|
3425
|
-
return new Promise((
|
|
3649
|
+
return new Promise((resolve9) => {
|
|
3426
3650
|
const isWin = process.platform === "win32";
|
|
3427
3651
|
const child = isWin ? spawn("cmd.exe", ["/c", "git", "rev-parse", "HEAD"], { cwd, windowsHide: true }) : spawn("git", ["rev-parse", "HEAD"], { cwd, shell: false });
|
|
3428
3652
|
let stdout2 = "";
|
|
@@ -3431,15 +3655,15 @@ function gitRevParseHead(cwd, timeoutMs = 1e4) {
|
|
|
3431
3655
|
});
|
|
3432
3656
|
const timer = setTimeout(() => {
|
|
3433
3657
|
child.kill("SIGKILL");
|
|
3434
|
-
|
|
3658
|
+
resolve9({ sha: "", exit: -1 });
|
|
3435
3659
|
}, timeoutMs);
|
|
3436
3660
|
child.on("error", () => {
|
|
3437
3661
|
clearTimeout(timer);
|
|
3438
|
-
|
|
3662
|
+
resolve9({ sha: "", exit: -1 });
|
|
3439
3663
|
});
|
|
3440
3664
|
child.on("close", (code) => {
|
|
3441
3665
|
clearTimeout(timer);
|
|
3442
|
-
|
|
3666
|
+
resolve9({ sha: stdout2.trim(), exit: code ?? -1 });
|
|
3443
3667
|
});
|
|
3444
3668
|
});
|
|
3445
3669
|
}
|
|
@@ -3779,7 +4003,7 @@ ${newEntry}
|
|
|
3779
4003
|
)
|
|
3780
4004
|
};
|
|
3781
4005
|
}
|
|
3782
|
-
const vr = await new Promise((
|
|
4006
|
+
const vr = await new Promise((resolve9) => {
|
|
3783
4007
|
const child = spawn(verifyShell, [verifyFlag, verifyLine], { cwd: ctx.cwd, windowsHide: true });
|
|
3784
4008
|
let stderr = "";
|
|
3785
4009
|
child.stderr?.setEncoding("utf8").on("data", (c) => {
|
|
@@ -3787,15 +4011,15 @@ ${newEntry}
|
|
|
3787
4011
|
});
|
|
3788
4012
|
const timer = setTimeout(() => {
|
|
3789
4013
|
child.kill("SIGKILL");
|
|
3790
|
-
|
|
4014
|
+
resolve9({ exitCode: -1, stderr: `${stderr}[timeout]` });
|
|
3791
4015
|
}, 15e3);
|
|
3792
4016
|
child.on("error", (e) => {
|
|
3793
4017
|
clearTimeout(timer);
|
|
3794
|
-
|
|
4018
|
+
resolve9({ exitCode: -1, stderr: e.message });
|
|
3795
4019
|
});
|
|
3796
4020
|
child.on("close", (code) => {
|
|
3797
4021
|
clearTimeout(timer);
|
|
3798
|
-
|
|
4022
|
+
resolve9({ exitCode: code ?? -1, stderr });
|
|
3799
4023
|
});
|
|
3800
4024
|
});
|
|
3801
4025
|
if (vr.exitCode !== 0) {
|
|
@@ -3927,7 +4151,7 @@ ${newEntry}
|
|
|
3927
4151
|
)
|
|
3928
4152
|
};
|
|
3929
4153
|
}
|
|
3930
|
-
const vr = await new Promise((
|
|
4154
|
+
const vr = await new Promise((resolve9) => {
|
|
3931
4155
|
const child = spawn(verifyShell, [verifyFlag, verifyLine], { cwd: ctx.cwd, windowsHide: true });
|
|
3932
4156
|
let stderr = "";
|
|
3933
4157
|
child.stderr?.setEncoding("utf8").on("data", (c) => {
|
|
@@ -3935,15 +4159,15 @@ ${newEntry}
|
|
|
3935
4159
|
});
|
|
3936
4160
|
const timer = setTimeout(() => {
|
|
3937
4161
|
child.kill("SIGKILL");
|
|
3938
|
-
|
|
4162
|
+
resolve9({ exitCode: -1, stderr: `${stderr}[timeout]` });
|
|
3939
4163
|
}, 15e3);
|
|
3940
4164
|
child.on("error", (e) => {
|
|
3941
4165
|
clearTimeout(timer);
|
|
3942
|
-
|
|
4166
|
+
resolve9({ exitCode: -1, stderr: e.message });
|
|
3943
4167
|
});
|
|
3944
4168
|
child.on("close", (code) => {
|
|
3945
4169
|
clearTimeout(timer);
|
|
3946
|
-
|
|
4170
|
+
resolve9({ exitCode: code ?? -1, stderr });
|
|
3947
4171
|
});
|
|
3948
4172
|
});
|
|
3949
4173
|
if (vr.exitCode !== 0) {
|
|
@@ -4515,7 +4739,7 @@ function normalizeEol(buf, eol) {
|
|
|
4515
4739
|
}
|
|
4516
4740
|
function registerRollback(program2) {
|
|
4517
4741
|
program2.command("rollback <timestamp>").description("Restore files from a backup snapshot (preserves original LF/CRLF)").action(async (timestamp) => {
|
|
4518
|
-
const dir =
|
|
4742
|
+
const dir = join(getBackupRoot(), timestamp);
|
|
4519
4743
|
const metaPath = join(dir, "metadata.json");
|
|
4520
4744
|
let meta;
|
|
4521
4745
|
try {
|
|
@@ -4557,6 +4781,82 @@ function registerRollback(program2) {
|
|
|
4557
4781
|
});
|
|
4558
4782
|
}
|
|
4559
4783
|
init_checkAgentTeams();
|
|
4784
|
+
var FLAT_LEGACY_DEPRECATED = /* @__PURE__ */ new Set(["plan-feature", "execute-task", "verify-work"]);
|
|
4785
|
+
var FLAT_LEGACY_KEEP = /* @__PURE__ */ new Set(["research", "retro"]);
|
|
4786
|
+
var NON_WORKFLOW_DIRS = /* @__PURE__ */ new Set(["disciplines", "judgments"]);
|
|
4787
|
+
async function scanWorkflowsNested(workflowsDir, entries) {
|
|
4788
|
+
const workflows = [];
|
|
4789
|
+
const deprecated = [];
|
|
4790
|
+
for (const entry of entries.sort()) {
|
|
4791
|
+
if (NON_WORKFLOW_DIRS.has(entry)) continue;
|
|
4792
|
+
const src = join(workflowsDir, entry);
|
|
4793
|
+
let s;
|
|
4794
|
+
try {
|
|
4795
|
+
s = await stat(src);
|
|
4796
|
+
} catch {
|
|
4797
|
+
continue;
|
|
4798
|
+
}
|
|
4799
|
+
if (!s.isDirectory()) continue;
|
|
4800
|
+
let hasFlatSkill = false;
|
|
4801
|
+
try {
|
|
4802
|
+
await stat(join(src, "SKILL.md"));
|
|
4803
|
+
hasFlatSkill = true;
|
|
4804
|
+
} catch {
|
|
4805
|
+
hasFlatSkill = false;
|
|
4806
|
+
}
|
|
4807
|
+
if (hasFlatSkill) {
|
|
4808
|
+
if (FLAT_LEGACY_DEPRECATED.has(entry)) {
|
|
4809
|
+
deprecated.push(entry);
|
|
4810
|
+
continue;
|
|
4811
|
+
}
|
|
4812
|
+
if (FLAT_LEGACY_KEEP.has(entry)) {
|
|
4813
|
+
workflows.push({ name: entry, relPath: entry, isMaster: false });
|
|
4814
|
+
continue;
|
|
4815
|
+
}
|
|
4816
|
+
workflows.push({ name: entry, relPath: entry, isMaster: false });
|
|
4817
|
+
continue;
|
|
4818
|
+
}
|
|
4819
|
+
let subEntries;
|
|
4820
|
+
try {
|
|
4821
|
+
subEntries = await readdir(src);
|
|
4822
|
+
} catch {
|
|
4823
|
+
continue;
|
|
4824
|
+
}
|
|
4825
|
+
for (const sub of subEntries.sort()) {
|
|
4826
|
+
const subDir = join(src, sub);
|
|
4827
|
+
let ss;
|
|
4828
|
+
try {
|
|
4829
|
+
ss = await stat(subDir);
|
|
4830
|
+
} catch {
|
|
4831
|
+
continue;
|
|
4832
|
+
}
|
|
4833
|
+
if (!ss.isDirectory()) continue;
|
|
4834
|
+
try {
|
|
4835
|
+
await stat(join(subDir, "SKILL.md"));
|
|
4836
|
+
} catch {
|
|
4837
|
+
continue;
|
|
4838
|
+
}
|
|
4839
|
+
const name = sub === "auto" ? entry : `${entry}-${sub}`;
|
|
4840
|
+
workflows.push({ name, relPath: `${entry}/${sub}`, isMaster: sub === "auto" });
|
|
4841
|
+
}
|
|
4842
|
+
}
|
|
4843
|
+
return { workflows, deprecated };
|
|
4844
|
+
}
|
|
4845
|
+
function renderDeprecationBlock(deprecated) {
|
|
4846
|
+
if (deprecated.length === 0) return "";
|
|
4847
|
+
return [
|
|
4848
|
+
"\u26A0\uFE0F v3.0 BREAKING \u2014 v2 legacy slash cmd deprecated:",
|
|
4849
|
+
" /plan-feature \u2192 /plan (master) | /plan-phase (sub)",
|
|
4850
|
+
" /execute-task \u2192 /task (master) | /task-{clarify,code,test,deliver} (sub)",
|
|
4851
|
+
" /verify-work \u2192 /verify (master) | /verify-{progress,paranoid,qa,security,design,simplify,multispec} (sub)",
|
|
4852
|
+
" /research, /retro \u4E0D\u53D8",
|
|
4853
|
+
" \u8BE6\u89C1 CHANGELOG [3.0.0]",
|
|
4854
|
+
` skipped install: ${deprecated.sort().join(", ")}`,
|
|
4855
|
+
""
|
|
4856
|
+
].join("\n");
|
|
4857
|
+
}
|
|
4858
|
+
|
|
4859
|
+
// src/cli/lib/setup-helpers.ts
|
|
4560
4860
|
var PHASE_212 = /* @__PURE__ */ new Set([
|
|
4561
4861
|
"cc-plugin-marketplace",
|
|
4562
4862
|
"git-clone-with-setup",
|
|
@@ -4576,18 +4876,7 @@ async function warnIfAgentTeamsMissing() {
|
|
|
4576
4876
|
);
|
|
4577
4877
|
}
|
|
4578
4878
|
async function scanWorkflowsWithSkill(workflowsDir, entries) {
|
|
4579
|
-
|
|
4580
|
-
for (const entry of entries.sort()) {
|
|
4581
|
-
const src = join(workflowsDir, entry);
|
|
4582
|
-
try {
|
|
4583
|
-
const s = await stat(src);
|
|
4584
|
-
if (!s.isDirectory()) continue;
|
|
4585
|
-
await stat(join(src, "SKILL.md"));
|
|
4586
|
-
out.push(entry);
|
|
4587
|
-
} catch {
|
|
4588
|
-
}
|
|
4589
|
-
}
|
|
4590
|
-
return out;
|
|
4879
|
+
return scanWorkflowsNested(workflowsDir, entries);
|
|
4591
4880
|
}
|
|
4592
4881
|
async function runStepBInstall(manifestPaths) {
|
|
4593
4882
|
const opts = {
|
|
@@ -4673,7 +4962,12 @@ function registerSetup(program2) {
|
|
|
4673
4962
|
console.error(`error: workflows directory not found at ${workflowsDir}`);
|
|
4674
4963
|
process.exit(1);
|
|
4675
4964
|
}
|
|
4676
|
-
const toInstall = await scanWorkflowsWithSkill(
|
|
4965
|
+
const { workflows: toInstall, deprecated } = await scanWorkflowsWithSkill(
|
|
4966
|
+
workflowsDir,
|
|
4967
|
+
entries
|
|
4968
|
+
);
|
|
4969
|
+
const depBlock = renderDeprecationBlock(deprecated);
|
|
4970
|
+
if (depBlock) console.log(depBlock);
|
|
4677
4971
|
if (toInstall.length === 0) {
|
|
4678
4972
|
console.log("setup: no workflow directories with SKILL.md found \u2014 nothing to install");
|
|
4679
4973
|
process.exit(2);
|
|
@@ -4682,22 +4976,24 @@ function registerSetup(program2) {
|
|
|
4682
4976
|
console.log(
|
|
4683
4977
|
`[dry-run] setup would install ${toInstall.length} workflow(s) to ${skillsBase}:`
|
|
4684
4978
|
);
|
|
4685
|
-
for (const
|
|
4686
|
-
|
|
4979
|
+
for (const wf of toInstall) {
|
|
4980
|
+
const masterTag = wf.isMaster ? " (master)" : "";
|
|
4981
|
+
console.log(` ${wf.name} \u2192 ${join(skillsBase, wf.name)}${masterTag}`);
|
|
4687
4982
|
}
|
|
4688
4983
|
console.log(` run without --dry-run to execute`);
|
|
4689
4984
|
process.exit(0);
|
|
4690
4985
|
}
|
|
4691
4986
|
let skillsInstalled = 0;
|
|
4692
|
-
for (const
|
|
4693
|
-
const src = join(workflowsDir,
|
|
4694
|
-
const dst = join(skillsBase, name);
|
|
4987
|
+
for (const wf of toInstall) {
|
|
4988
|
+
const src = join(workflowsDir, wf.relPath);
|
|
4989
|
+
const dst = join(skillsBase, wf.name);
|
|
4695
4990
|
try {
|
|
4696
4991
|
await cp(src, dst, { recursive: true, force: true });
|
|
4697
|
-
|
|
4992
|
+
const masterTag = wf.isMaster ? " (master)" : "";
|
|
4993
|
+
console.log(` [A] installed ${wf.name} \u2192 ${dst}${masterTag}`);
|
|
4698
4994
|
skillsInstalled++;
|
|
4699
4995
|
} catch (e) {
|
|
4700
|
-
console.error(` error: failed to copy ${name}: ${e.message}`);
|
|
4996
|
+
console.error(` error: failed to copy ${wf.name}: ${e.message}`);
|
|
4701
4997
|
process.exit(1);
|
|
4702
4998
|
}
|
|
4703
4999
|
}
|
|
@@ -4958,7 +5254,7 @@ var uninstallNpmCli = async (ctx) => {
|
|
|
4958
5254
|
const m = install.cmd.match(/npm\s+(?:install|i)\s+(?:-g\s+)?(\S+)/);
|
|
4959
5255
|
const pkg = m?.[1] ?? ctx.manifest.metadata.upstream.source;
|
|
4960
5256
|
const isWin = process.platform === "win32";
|
|
4961
|
-
const result = await new Promise((
|
|
5257
|
+
const result = await new Promise((resolve9) => {
|
|
4962
5258
|
const child = isWin ? spawn("cmd.exe", ["/c", "npm", "uninstall", "-g", pkg], { windowsHide: true }) : spawn("npm", ["uninstall", "-g", pkg], { shell: false });
|
|
4963
5259
|
let stderr = "";
|
|
4964
5260
|
child.stderr?.setEncoding("utf8").on("data", (c) => {
|
|
@@ -4966,15 +5262,15 @@ var uninstallNpmCli = async (ctx) => {
|
|
|
4966
5262
|
});
|
|
4967
5263
|
const timer = setTimeout(() => {
|
|
4968
5264
|
child.kill("SIGKILL");
|
|
4969
|
-
|
|
5265
|
+
resolve9({ exitCode: -1, stderr: `${stderr}[timeout]` });
|
|
4970
5266
|
}, 3e4);
|
|
4971
5267
|
child.on("error", (e) => {
|
|
4972
5268
|
clearTimeout(timer);
|
|
4973
|
-
|
|
5269
|
+
resolve9({ exitCode: -1, stderr: e.message });
|
|
4974
5270
|
});
|
|
4975
5271
|
child.on("close", (code) => {
|
|
4976
5272
|
clearTimeout(timer);
|
|
4977
|
-
|
|
5273
|
+
resolve9({ exitCode: code ?? -1, stderr });
|
|
4978
5274
|
});
|
|
4979
5275
|
});
|
|
4980
5276
|
if (result.exitCode !== 0) {
|