@mstar-harness/engine 2.1.1 → 2.3.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 +1 -1
- package/dist/engine.js +64 -11
- package/dist/host.d.ts +24 -14
- package/dist/index.d.ts +2 -2
- package/dist/lint.d.ts +43 -0
- package/dist/path.d.ts +21 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@ const version = readHarnessVersion(); // "1.8.8" — monorepo root package.json
|
|
|
19
19
|
## Scope
|
|
20
20
|
|
|
21
21
|
- Importable library only — **no `bin`**; the CLI (`@mstar-harness/cli`) wraps engine functions as thin `mstar …` subcommands.
|
|
22
|
-
- Dependencies: `node:*` only (zero external runtime deps — all validators hand-rolled; ajv pruned as phantom
|
|
22
|
+
- Dependencies: `node:*` only (zero external runtime deps — all validators hand-rolled; ajv and zod were pruned as phantom dependencies).
|
|
23
23
|
- Skill prose stays authoritative; engine exports are the machine-checkable mirror of the rules the `mstar-*` skills state.
|
|
24
24
|
|
|
25
25
|
## License
|
package/dist/engine.js
CHANGED
|
@@ -83,24 +83,52 @@ function readHarnessVersion() {
|
|
|
83
83
|
}
|
|
84
84
|
// src/path.ts
|
|
85
85
|
import { mkdirSync as mkdirSync2, readdirSync, readFileSync as readFileSync2, statSync } from "node:fs";
|
|
86
|
+
import { execFileSync } from "node:child_process";
|
|
86
87
|
import { basename as basename2, dirname as dirname2, isAbsolute, join as join2, relative, resolve as resolve2 } from "node:path";
|
|
87
88
|
function resolveHarnessDir(startDir = process.cwd(), opts = {}) {
|
|
88
89
|
const start = resolve2(startDir);
|
|
89
90
|
const explicit = opts.harnessDir ?? process.env.MSTAR_HARNESS_DIR;
|
|
90
91
|
if (explicit)
|
|
91
92
|
return resolve2(start, explicit);
|
|
93
|
+
const boundary = resolve2(start, opts.workspaceRoot ?? defaultWorkspaceRoot(start));
|
|
92
94
|
let dir = start;
|
|
93
95
|
for (;; ) {
|
|
96
|
+
if (!isAtOrBelow(dir, boundary))
|
|
97
|
+
return null;
|
|
94
98
|
for (const candidate of [join2(dir, ".mstar"), join2(dir, ".agents"), join2(dir, ".plans"), join2(dir, "plans")]) {
|
|
95
99
|
if (isDirectory(candidate))
|
|
96
100
|
return candidate;
|
|
97
101
|
}
|
|
102
|
+
if (dir === boundary)
|
|
103
|
+
return null;
|
|
98
104
|
const parent = dirname2(dir);
|
|
99
105
|
if (parent === dir)
|
|
100
106
|
return null;
|
|
101
107
|
dir = parent;
|
|
102
108
|
}
|
|
103
109
|
}
|
|
110
|
+
function defaultWorkspaceRoot(startDir) {
|
|
111
|
+
try {
|
|
112
|
+
const cdup = execFileSync("git", ["rev-parse", "--show-cdup"], {
|
|
113
|
+
cwd: startDir,
|
|
114
|
+
encoding: "utf8",
|
|
115
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
116
|
+
}).trim();
|
|
117
|
+
if (!cdup)
|
|
118
|
+
return startDir;
|
|
119
|
+
let boundary = startDir;
|
|
120
|
+
for (const segment of cdup.split(/[\\/]/)) {
|
|
121
|
+
if (segment && segment !== ".")
|
|
122
|
+
boundary = dirname2(boundary);
|
|
123
|
+
}
|
|
124
|
+
return resolve2(boundary);
|
|
125
|
+
} catch {}
|
|
126
|
+
return startDir;
|
|
127
|
+
}
|
|
128
|
+
function isAtOrBelow(dir, root) {
|
|
129
|
+
const rel = relative(root, dir);
|
|
130
|
+
return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
|
|
131
|
+
}
|
|
104
132
|
function resolveSpecsDir(harnessDir, opts = {}) {
|
|
105
133
|
const harness = resolve2(harnessDir);
|
|
106
134
|
const repoRoot = dirname2(harness);
|
|
@@ -646,7 +674,7 @@ function parseAssignmentBranchForms(assignmentText) {
|
|
|
646
674
|
if (fields.branchPolicy !== undefined && fields.branchPolicy !== "") {
|
|
647
675
|
const direct = fields.branchPolicy.match(/^direct\s+on\s+(\S+)/i);
|
|
648
676
|
if (direct) {
|
|
649
|
-
const strict = fields.branchPolicy.match(/^direct\s+on\s+(\S+)(?:\s*(?:[
|
|
677
|
+
const strict = fields.branchPolicy.match(/^direct\s+on\s+(\S+)(?:\s*(?:[\u2014\u2013]|--|-)\s*(.+))?$/);
|
|
650
678
|
forms.directOn = { branch: direct[1].trim(), reason: strict ? (strict[2] ?? "").trim() : "" };
|
|
651
679
|
}
|
|
652
680
|
}
|
|
@@ -1150,7 +1178,7 @@ function techDebtRollup(docOrPath) {
|
|
|
1150
1178
|
return { computed, stored, checks, overall };
|
|
1151
1179
|
}
|
|
1152
1180
|
// src/worktree.ts
|
|
1153
|
-
import { execFileSync } from "node:child_process";
|
|
1181
|
+
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
1154
1182
|
import { existsSync as existsSync3 } from "node:fs";
|
|
1155
1183
|
import { isAbsolute as isAbsolute3, resolve as resolve5 } from "node:path";
|
|
1156
1184
|
var DEFAULT_PROBE_TIMEOUT_MS = 1e4;
|
|
@@ -1173,7 +1201,7 @@ function probeBranch(worktreePath, opts) {
|
|
|
1173
1201
|
return { branch: precomputed };
|
|
1174
1202
|
const timeout = opts.timeoutMs ?? probeTimeoutMs();
|
|
1175
1203
|
try {
|
|
1176
|
-
const stdout =
|
|
1204
|
+
const stdout = execFileSync2(opts.gitPath ?? "git", ["-C", worktreePath, "branch", "--show-current"], {
|
|
1177
1205
|
encoding: "utf8",
|
|
1178
1206
|
stdio: ["ignore", "pipe", "pipe"],
|
|
1179
1207
|
timeout
|
|
@@ -1302,7 +1330,7 @@ function singleReviewSnapshot(assignments) {
|
|
|
1302
1330
|
return gate(violations);
|
|
1303
1331
|
}
|
|
1304
1332
|
// src/sdd.ts
|
|
1305
|
-
import { execFileSync as
|
|
1333
|
+
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
1306
1334
|
import { mkdirSync as mkdirSync4, readFileSync as readFileSync4, realpathSync, statSync as statSync3, writeFileSync as writeFileSync3 } from "node:fs";
|
|
1307
1335
|
import { basename as basename3, dirname as dirname4, isAbsolute as isAbsolute4, join as join5, resolve as resolve6 } from "node:path";
|
|
1308
1336
|
class SddScriptError extends Error {
|
|
@@ -1330,7 +1358,7 @@ function isFile(file) {
|
|
|
1330
1358
|
var GIT_CAPTURE_MAX_BYTES = 64 * 1024 * 1024;
|
|
1331
1359
|
function gitOut(cwd, args) {
|
|
1332
1360
|
try {
|
|
1333
|
-
return
|
|
1361
|
+
return execFileSync3("git", args, {
|
|
1334
1362
|
cwd,
|
|
1335
1363
|
encoding: "utf8",
|
|
1336
1364
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -1463,7 +1491,7 @@ function reviewPackage(base, head, outFile, opts = {}) {
|
|
|
1463
1491
|
const cwd = opts.cwd ?? process.cwd();
|
|
1464
1492
|
const verifyRef = (ref, what) => {
|
|
1465
1493
|
try {
|
|
1466
|
-
|
|
1494
|
+
execFileSync3("git", ["rev-parse", "--verify", "--quiet", ref], { cwd, stdio: ["ignore", "pipe", "pipe"] });
|
|
1467
1495
|
} catch {
|
|
1468
1496
|
throw new SddScriptError(`bad ${what}: ${ref}`, 2);
|
|
1469
1497
|
}
|
|
@@ -1483,7 +1511,7 @@ function reviewPackage(base, head, outFile, opts = {}) {
|
|
|
1483
1511
|
const shortHead = gitOut(cwd, ["rev-parse", "--short", head]) ?? head;
|
|
1484
1512
|
out = join5(sddDir, `review-${shortBase}..${shortHead}.diff`);
|
|
1485
1513
|
}
|
|
1486
|
-
const run = (args) =>
|
|
1514
|
+
const run = (args) => execFileSync3("git", args, { cwd, maxBuffer: GIT_CAPTURE_MAX_BYTES });
|
|
1487
1515
|
const parts = [
|
|
1488
1516
|
Buffer.from(`# Review package: ${base}..${head}
|
|
1489
1517
|
|
|
@@ -1507,7 +1535,7 @@ function assertBaseSha(ref, opts = {}) {
|
|
|
1507
1535
|
throw new SddScriptError(`assertBaseSha: BASE must be a commit SHA (full or prefix); got ${JSON.stringify(ref)}. ` + "Never use HEAD~1 as review BASE (multi-commit tasks truncate).", 2);
|
|
1508
1536
|
}
|
|
1509
1537
|
try {
|
|
1510
|
-
|
|
1538
|
+
execFileSync3("git", ["rev-parse", "--verify", "--quiet", `${ref}^{commit}`], {
|
|
1511
1539
|
cwd: opts.cwd,
|
|
1512
1540
|
stdio: ["ignore", "pipe", "pipe"]
|
|
1513
1541
|
});
|
|
@@ -2524,6 +2552,7 @@ function renderIndex(params) {
|
|
|
2524
2552
|
if (rejectedRows !== "") {
|
|
2525
2553
|
sections.push("", "## Findings considered and rejected", "", rejectedRows);
|
|
2526
2554
|
}
|
|
2555
|
+
sections.push("", "## Red-team dispositions", "", "- <finding>: <survived / refuted / hallucination-dropped / uncovered-kept>, <one-line reason>");
|
|
2527
2556
|
return `${sections.join(`
|
|
2528
2557
|
`)}
|
|
2529
2558
|
`;
|
|
@@ -3052,11 +3081,31 @@ function findTemporaryMarkers(fileText) {
|
|
|
3052
3081
|
}
|
|
3053
3082
|
return { ok: violations.length === 0, violations, markers };
|
|
3054
3083
|
}
|
|
3084
|
+
var TASK_ARTIFACT_RE = /\btask-\d+(?:-(?:brief|report|fix-report|diff)|\.diff)\b/g;
|
|
3085
|
+
var SDD_DEEPLINK_RE = /\.(?:mstar|agents)\/sdd\/([^\s/<>{}\[\]"'\*\?]+)/g;
|
|
3086
|
+
function findEphemeralCitations(skillText) {
|
|
3087
|
+
const citations = [];
|
|
3088
|
+
const lines = skillText.split(/\r?\n/);
|
|
3089
|
+
for (let i = 0;i < lines.length; i++) {
|
|
3090
|
+
const found = [];
|
|
3091
|
+
for (const m of lines[i].matchAll(TASK_ARTIFACT_RE)) {
|
|
3092
|
+
found.push({ index: m.index, match: m[0], kind: "task-artifact" });
|
|
3093
|
+
}
|
|
3094
|
+
for (const m of lines[i].matchAll(SDD_DEEPLINK_RE)) {
|
|
3095
|
+
found.push({ index: m.index, match: m[0], kind: "sdd-deeplink" });
|
|
3096
|
+
}
|
|
3097
|
+
found.sort((a, b) => a.index - b.index);
|
|
3098
|
+
for (const f of found) {
|
|
3099
|
+
citations.push({ line: i + 1, match: f.match, kind: f.kind });
|
|
3100
|
+
}
|
|
3101
|
+
}
|
|
3102
|
+
return citations;
|
|
3103
|
+
}
|
|
3055
3104
|
var TEST_FILE_PATH_RE = /[\w./-]+\.(?:test|spec)\.[a-z0-9]+/i;
|
|
3056
3105
|
var TEST_FILE_PHRASE_RE = /\btest files?\b/i;
|
|
3057
3106
|
var COMMAND_PROMPT_RE = /^\s*[$>]\s*\S/;
|
|
3058
3107
|
var RUNNER_RE = /\b(?:bun|pnpm|npm|yarn|npx|bunx)\s+(?:test|run|exec)\b|\b(?:npx|bunx)\s+[\w./-]+\b|\b(?:tsc|vitest|jest|mocha|pytest)\b|\bgo\s+test\b|\bcargo\s+test\b/i;
|
|
3059
|
-
var OUTPUT_TOKEN_RE = /[
|
|
3108
|
+
var OUTPUT_TOKEN_RE = /[\u2713\u2714\u2717\u2718]|\b(?:PASS|FAIL)\b|\b\d+\s+(?:pass(?:es|ed)?|fail(?:s|ed|ing)?|skipped|tests?|ok)\b|\bok\s+\d+\b|\ball\s+ok\b|exit(?:ed)?\s+(?:with\s+)?(?:code\s+)?\d+/i;
|
|
3060
3109
|
function assertSddTddTriple(reportText) {
|
|
3061
3110
|
const violations = [];
|
|
3062
3111
|
const lines = reportText.split(/\r?\n/);
|
|
@@ -3377,6 +3426,8 @@ function detectHost(signals) {
|
|
|
3377
3426
|
return "opencode";
|
|
3378
3427
|
if (s.has("task_agent_batch") || s.has("ask") || s.has("hub"))
|
|
3379
3428
|
return "omp";
|
|
3429
|
+
if (s.has("subagent"))
|
|
3430
|
+
return "dsh";
|
|
3380
3431
|
if (s.has("AgentSwarm"))
|
|
3381
3432
|
return "kimi";
|
|
3382
3433
|
if (s.has("Agent") || s.has("AskUserQuestion") || s.has("EnterPlanMode") || s.has("TodoWrite"))
|
|
@@ -3400,9 +3451,10 @@ function resolveSkillRoot(host, paths) {
|
|
|
3400
3451
|
case "kimi":
|
|
3401
3452
|
case "zcode":
|
|
3402
3453
|
return `./skills/${skill}${suffix}`;
|
|
3403
|
-
case "pi":
|
|
3404
3454
|
case "dsh":
|
|
3405
|
-
return
|
|
3455
|
+
return `$DSH_BUNDLED_SKILL_DIR/${skill}${suffix}`;
|
|
3456
|
+
case "pi":
|
|
3457
|
+
return `deferred: pi has no plugin API in v1 — skill-root resolution lands with its adapter (roadmap §8.4)`;
|
|
3406
3458
|
}
|
|
3407
3459
|
}
|
|
3408
3460
|
// src/skill-authoring.ts
|
|
@@ -3495,6 +3547,7 @@ export {
|
|
|
3495
3547
|
findingsCleanupGate,
|
|
3496
3548
|
findTemporaryMarkers,
|
|
3497
3549
|
findSimplifyMarkers,
|
|
3550
|
+
findEphemeralCitations,
|
|
3498
3551
|
executionModeToN,
|
|
3499
3552
|
evaluatePhaseGate,
|
|
3500
3553
|
emitGitignoreSnippet,
|
package/dist/host.d.ts
CHANGED
|
@@ -11,7 +11,10 @@
|
|
|
11
11
|
* resolution.
|
|
12
12
|
* - `.harness/references/skill-programmatic-roadmap.md` §8.4 — the
|
|
13
13
|
* `HostAdapter` shared contract (all hooks optional; no concrete adapters
|
|
14
|
-
* in the engine; pi
|
|
14
|
+
* in the engine; pi deferred).
|
|
15
|
+
* - `.harness/references/dsh-adapter-roadmap.md` §4 D5 (this iteration) —
|
|
16
|
+
* the dsh detection row + skill-root form; mirror text lands in the
|
|
17
|
+
* mstar-host skill when this module is upstreamed.
|
|
15
18
|
*
|
|
16
19
|
* UX judgment (ambiguous-host fallback reasoning, plan-mode bridges) stays
|
|
17
20
|
* prompt — this module only turns tool shapes into a host id.
|
|
@@ -19,18 +22,19 @@
|
|
|
19
22
|
import type { GateResult, ValidationResult } from "./core.js";
|
|
20
23
|
import type { AssignmentFields } from "./dispatch.js";
|
|
21
24
|
import type { IntegrationMergeLease } from "./lease.js";
|
|
22
|
-
/** All hosts the engine knows about (roadmap §8.4 host union). `pi`
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
+
/** All hosts the engine knows about (roadmap §8.4 host union). `pi` has no
|
|
26
|
+
* plugin API in v1 — it appears in the union (and in `HostAdapter.host`) but
|
|
27
|
+
* is never detected and gets no adapter. `dsh` is detected and resolved
|
|
28
|
+
* (roadmap §4 D5, this iteration); its adapter ships in the dsh plugin. */
|
|
25
29
|
export type HostId = "opencode" | "omp" | "pi" | "dsh" | "cursor" | "codex" | "kimi" | "zcode";
|
|
26
|
-
/** Result of `detectHost`: one of the
|
|
30
|
+
/** Result of `detectHost`: one of the seven known hosts or `ambiguous`
|
|
27
31
|
* (prompt judgment then applies per mstar-host). */
|
|
28
|
-
export type DetectResult = "opencode" | "omp" | "cursor" | "codex" | "kimi" | "zcode" | "ambiguous";
|
|
32
|
+
export type DetectResult = "opencode" | "omp" | "dsh" | "cursor" | "codex" | "kimi" | "zcode" | "ambiguous";
|
|
29
33
|
/** Tool-shape signal tokens accepted by `detectHost`, derived from the
|
|
30
34
|
* mstar-host detection table. Plan-mode extras (CreatePlan/SwitchMode) and
|
|
31
35
|
* Browser-plugin tools are documented in the table but are not part of the
|
|
32
36
|
* v1 signal enum. */
|
|
33
|
-
export type ToolSignal = "subagent_type" | "question" | "task_subagent" | "task_agent_batch" | "ask" | "hub" | "Agent" | "AgentSwarm" | "AskUserQuestion" | "EnterPlanMode" | "TodoWrite" | "plan_slash" | "goal" | "functions.*" | "tool_search";
|
|
37
|
+
export type ToolSignal = "subagent_type" | "question" | "task_subagent" | "task_agent_batch" | "ask" | "hub" | "subagent" | "Agent" | "AgentSwarm" | "AskUserQuestion" | "EnterPlanMode" | "TodoWrite" | "plan_slash" | "goal" | "functions.*" | "tool_search";
|
|
34
38
|
/**
|
|
35
39
|
* Detect the active host from session tool shapes, per the ordered
|
|
36
40
|
* mstar-host table (ported verbatim):
|
|
@@ -40,14 +44,19 @@ export type ToolSignal = "subagent_type" | "question" | "task_subagent" | "task_
|
|
|
40
44
|
* | `subagent_type` (Task param; plan mode + CreatePlan/SwitchMode) | cursor |
|
|
41
45
|
* | `question`, or `task_subagent` (task tool, singular subagent, no batch) | opencode |
|
|
42
46
|
* | `task_agent_batch` (task tool, agent/tasks[] batch), `ask`, `hub` | omp |
|
|
47
|
+
* | `subagent` (dsh's model-facing delegation tool) | dsh |
|
|
43
48
|
* | `Agent`/`AskUserQuestion`/`EnterPlanMode` + `AgentSwarm` (Kimi-only) | kimi |
|
|
44
49
|
* | `Agent`/`AskUserQuestion`/`EnterPlanMode`/`TodoWrite`, no `AgentSwarm` | zcode |
|
|
45
50
|
* | `/plan`, `/goal`; Goal tools; `functions.*` namespaces; `tool_search` | codex |
|
|
46
51
|
*
|
|
47
|
-
* Order matters: cursor → opencode → omp → kimi → zcode → codex — the
|
|
48
|
-
* sharpest Task-based split is `subagent_type` (Cursor) vs `
|
|
49
|
-
* (OpenCode) vs `agent`/`tasks[]` (omp)
|
|
50
|
-
*
|
|
52
|
+
* Order matters: cursor → opencode → omp → dsh → kimi → zcode → codex — the
|
|
53
|
+
* sharpest Task-based split is `subagent_type` (Cursor) vs `task_subagent`
|
|
54
|
+
* (OpenCode) vs `agent`/`tasks[]` (omp); dsh's `subagent` tool (roadmap §4
|
|
55
|
+
* D5) collides with no other row, so it sits with the agent-tool hosts.
|
|
56
|
+
* (`mstar-host` skill prose still says colloquial `subagent` for OpenCode —
|
|
57
|
+
* disambiguate it to `task_subagent` in the same upstream PR that carries
|
|
58
|
+
* this row, together with the CLI `HOST_SIGNALS` list.)
|
|
59
|
+
* Still ambiguous → `"ambiguous"` (prompt judgment stays in the skill).
|
|
51
60
|
*/
|
|
52
61
|
export declare function detectHost(signals: readonly ToolSignal[]): DetectResult;
|
|
53
62
|
/** Skill name + optional skill-relative path for skill-root resolution. */
|
|
@@ -69,7 +78,8 @@ export type SkillRootPaths = {
|
|
|
69
78
|
* | codex | `skills/<name>[/<rel>]` (plugin-mounted; project command skills under `.agents/skills/<name>/`) |
|
|
70
79
|
* | opencode | `harness-skills/<name>[/<rel>]` (package-internal via `@mstar-harness/opencode` — never `process.cwd()/skills/`) |
|
|
71
80
|
* | kimi / zcode | `./skills/<name>[/<rel>]` (plugin mount from the installed plugin root) |
|
|
72
|
-
* |
|
|
81
|
+
* | dsh | `$DSH_BUNDLED_SKILL_DIR/<name>[/<rel>]` (skill-local bundled root — dsh-skill-local `bundledSkillDir` default; the single canonical mount per roadmap D6. Frozen this iteration; local dev mounts the mirror `skills/` via `customSkillDirs` instead, but the canonical published form stays the bundled root) |
|
|
82
|
+
* | pi | deferred — no plugin API in v1 (roadmap §8.4) |
|
|
73
83
|
*/
|
|
74
84
|
export declare function resolveSkillRoot(host: HostId, paths: SkillRootPaths): string;
|
|
75
85
|
/**
|
|
@@ -79,8 +89,8 @@ export declare function resolveSkillRoot(host: HostId, paths: SkillRootPaths): s
|
|
|
79
89
|
* rule: skill text remains authoritative; the engine only returns results
|
|
80
90
|
* the caller chooses to honor). `log` is required — adapters must be able to
|
|
81
91
|
* report. No concrete adapters ship in the engine: opencode binds via its
|
|
82
|
-
* own plugin code, omp via the command layer
|
|
83
|
-
* until
|
|
92
|
+
* own plugin code, omp via the command layer, dsh via the dsh plugin (this
|
|
93
|
+
* iteration); pi stays deferred until its plugin API lands.
|
|
84
94
|
*/
|
|
85
95
|
export interface HostAdapter {
|
|
86
96
|
host: "opencode" | "omp" | "pi" | "dsh" | "cursor" | "codex" | "kimi" | "zcode";
|
package/dist/index.d.ts
CHANGED
|
@@ -42,8 +42,8 @@ export type { AuditCategory, AuditEffort, AuditFinding, AuditPriority, AuditRisk
|
|
|
42
42
|
export { AUDIT_CATEGORIES, AUDIT_EFFORTS, AUDIT_PRIORITIES, AUDIT_RISKS, redactSecrets, scaffoldAuditPlan, validateAuditStatusBlocks, } from "./audit.js";
|
|
43
43
|
export type { ReferenceCheckResult } from "./compound.js";
|
|
44
44
|
export { KNOWLEDGE_BUG_PROBLEM_TYPES, KNOWLEDGE_CATEGORY_MAP, KNOWLEDGE_KNOWLEDGE_PROBLEM_TYPES, KNOWLEDGE_PROBLEM_TYPES, KNOWLEDGE_REQUIRED_FIELDS, KNOWLEDGE_RESOLUTION_TYPES, KNOWLEDGE_SEVERITIES, assertIndexRows, compoundRefreshScope, referenceExists, scopeGuard, validateSchemaYaml, } from "./compound.js";
|
|
45
|
-
export type { PlanQualityFinding, PlanQualityResult, SimplifyMarker, TemporaryMarker, TemporaryMarkerResult, } from "./lint.js";
|
|
46
|
-
export { assertSddTddTriple, findSimplifyMarkers, findTemporaryMarkers, lintSkillFrontmatter, lintStrategySections, planQualityBar, } from "./lint.js";
|
|
45
|
+
export type { EphemeralCitation, PlanQualityFinding, PlanQualityResult, SimplifyMarker, TemporaryMarker, TemporaryMarkerResult, } from "./lint.js";
|
|
46
|
+
export { assertSddTddTriple, findEphemeralCitations, findSimplifyMarkers, findTemporaryMarkers, lintSkillFrontmatter, lintStrategySections, planQualityBar, } from "./lint.js";
|
|
47
47
|
export type { DevTrackParam, QcReviewerParam, RoleFamily, RoleMappingEntry, RoleMappingOptions, } from "./roles.js";
|
|
48
48
|
export { DEV_TRACK_PARAMS, QC_REVIEWER_PARAMS, ROLE_MAPPING, SHARED_FAMILIES, lintLoadOrder, validateRoleMapping, } from "./roles.js";
|
|
49
49
|
export type { DetectResult, HostAdapter, HostId, SkillRootPaths, ToolSignal } from "./host.js";
|
package/dist/lint.d.ts
CHANGED
|
@@ -27,6 +27,10 @@
|
|
|
27
27
|
* contract (not a workflow summary), third person.
|
|
28
28
|
* - STRATEGY.md structure: `mstar-strategy` SKILL.md § STRATEGY.md structure —
|
|
29
29
|
* six required sections.
|
|
30
|
+
* - Ephemeral citations: knowledge `conventions/skill-content-porting-discipline.md`
|
|
31
|
+
* §3 ("No ephemeral citations in durable skill text") + session evaluation
|
|
32
|
+
* 2026-08-16 discrimination contract — concrete task-artifact references
|
|
33
|
+
* and SDD deeplinks are ephemeral; placeholder forms are not.
|
|
30
34
|
*
|
|
31
35
|
* Enforcement depth: roadmap §8.5 C4 — v1 lints are non-blocking
|
|
32
36
|
* `ValidationResult`s; callers surface them as warnings.
|
|
@@ -95,6 +99,45 @@ export type TemporaryMarkerResult = GateResult & {
|
|
|
95
99
|
* carry); do not special-case it without a real FP report.
|
|
96
100
|
*/
|
|
97
101
|
export declare function findTemporaryMarkers(fileText: string): TemporaryMarkerResult;
|
|
102
|
+
/**
|
|
103
|
+
* An ephemeral citation found in durable skill text: a concrete reference to
|
|
104
|
+
* a per-task artifact or an SDD deeplink that survives nothing (knowledge
|
|
105
|
+
* conventions/skill-content-porting-discipline.md §3 — "No ephemeral
|
|
106
|
+
* citations in durable skill text": a calibration line citing an SDD task
|
|
107
|
+
* report violates standalone + survives nothing; instances/examples cite
|
|
108
|
+
* in-repo artifacts only).
|
|
109
|
+
*/
|
|
110
|
+
export type EphemeralCitation = {
|
|
111
|
+
/** 1-based line number of the citation. */
|
|
112
|
+
line: number;
|
|
113
|
+
/** The matched citation token (artifact name or deeplink prefix). */
|
|
114
|
+
match: string;
|
|
115
|
+
/** `task-artifact`: `task-<digits>-(brief|report|fix-report|diff)`;
|
|
116
|
+
* `sdd-deeplink`: `.mstar/sdd/` / `.agents/sdd/` + a concrete first
|
|
117
|
+
* segment. */
|
|
118
|
+
kind: "task-artifact" | "sdd-deeplink";
|
|
119
|
+
};
|
|
120
|
+
/**
|
|
121
|
+
* Find ephemeral citations in skill text (knowledge
|
|
122
|
+
* conventions/skill-content-porting-discipline.md §3 + session evaluation
|
|
123
|
+
* 2026-08-16 discrimination contract).
|
|
124
|
+
*
|
|
125
|
+
* Discrimination (HARD — zero false positives on the skills corpus):
|
|
126
|
+
* - `task-<digits>-(brief|report|fix-report|diff)` with 1+ digits is a
|
|
127
|
+
* concrete instance → reported (`task-2-report`, `task-1.diff`).
|
|
128
|
+
* Placeholders (`task-N-brief`, `task-N-report`, `<plan-id>`,
|
|
129
|
+
* `{SDD_DIR}/task-N-report.md`) never match.
|
|
130
|
+
* - `.mstar/sdd/<segment>` / `.agents/sdd/<segment>` with a concrete first
|
|
131
|
+
* segment (`20260815-x`) → reported; `<plan-id>` / `{SDD_DIR}` segments
|
|
132
|
+
* are template forms → never match.
|
|
133
|
+
*
|
|
134
|
+
* Discovery only — a finder returning an array, same shape as
|
|
135
|
+
* `findSimplifyMarkers`, NOT a GateResult; callers wrap findings into
|
|
136
|
+
* `ViolationResult`s (codes `skill.ephemeral.task-artifact` /
|
|
137
|
+
* `skill.ephemeral.sdd-deeplink`). Citations are reported line by line in
|
|
138
|
+
* 1-based line order, source order within a line.
|
|
139
|
+
*/
|
|
140
|
+
export declare function findEphemeralCitations(skillText: string): EphemeralCitation[];
|
|
98
141
|
/**
|
|
99
142
|
* Assert the SDD TDD triple is present in a `task-N-report.md` text
|
|
100
143
|
* (mstar-coding-behavior § Integration Notes — "completion evidence must
|
package/dist/path.d.ts
CHANGED
|
@@ -10,15 +10,32 @@ export type ResolveHarnessDirOptions = {
|
|
|
10
10
|
* (the caller may scaffold it).
|
|
11
11
|
*/
|
|
12
12
|
harnessDir?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Workspace-root stop boundary (roadmap §7c / plan
|
|
15
|
+
* 20260810-harness-root-boundary). The upward probe keeps walking only
|
|
16
|
+
* while `dir` is at or below this root — a harness dir above it is never
|
|
17
|
+
* returned (the `~/.mstar` global-collision defect is the special case).
|
|
18
|
+
* Resolved against `startDir` when relative. When omitted, the default
|
|
19
|
+
* boundary is the git top-level of `startDir` (sync `git rev-parse
|
|
20
|
+
* --show-cdup`; on failure / non-git start it falls back to
|
|
21
|
+
* `startDir` itself — a non-git start probes only itself, never upward;
|
|
22
|
+
* deliberate tightening). The boundary is an explicit caller value: the
|
|
23
|
+
* engine git-probes only for this default resolution, never during the
|
|
24
|
+
* walk.
|
|
25
|
+
*/
|
|
26
|
+
workspaceRoot?: string;
|
|
13
27
|
};
|
|
14
28
|
/**
|
|
15
29
|
* Resolve `{HARNESS_DIR}` per plan-conventions § {HARNESS_DIR} 解析顺序
|
|
16
30
|
* (find-first-stop): `.mstar/` → `.agents/` → `.plans/`/`plans/`, walking up
|
|
17
|
-
* from `startDir
|
|
18
|
-
*
|
|
19
|
-
*
|
|
31
|
+
* from `startDir` but NEVER above the workspace root (`opts.workspaceRoot`,
|
|
32
|
+
* default = git top-level of `startDir`). Harness candidates are
|
|
33
|
+
* dir-existence (the empty-dir rule applies to `{SPECS_DIR}` only). An
|
|
34
|
+
* explicit override via `opts.harnessDir` or `MSTAR_HARNESS_DIR` wins over
|
|
35
|
+
* probing and short-circuits before any boundary logic.
|
|
20
36
|
*
|
|
21
|
-
* Returns the absolute harness dir, or `null` when no candidate exists
|
|
37
|
+
* Returns the absolute harness dir, or `null` when no candidate exists
|
|
38
|
+
* within the workspace boundary.
|
|
22
39
|
*/
|
|
23
40
|
export declare function resolveHarnessDir(startDir?: string, opts?: ResolveHarnessDirOptions): string | null;
|
|
24
41
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mstar-harness/engine",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "Morning Star Harness Workflow Engine — deterministic workflow enforcement library (path, status, lease, dispatch, sdd, iteration, lint gates).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|