@outcomeeng/spx 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +82 -50
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1707,6 +1707,17 @@ var PRIORITY_ORDER = {
|
|
|
1707
1707
|
low: 2
|
|
1708
1708
|
};
|
|
1709
1709
|
var DEFAULT_PRIORITY = "medium";
|
|
1710
|
+
var SESSION_FRONT_MATTER = {
|
|
1711
|
+
PRIORITY: "priority",
|
|
1712
|
+
TAGS: "tags",
|
|
1713
|
+
ID: "id",
|
|
1714
|
+
BRANCH: "branch",
|
|
1715
|
+
CREATED_AT: "created_at",
|
|
1716
|
+
AGENT_SESSION_ID: "agent_session_id",
|
|
1717
|
+
WORKING_DIRECTORY: "working_directory",
|
|
1718
|
+
SPECS: "specs",
|
|
1719
|
+
FILES: "files"
|
|
1720
|
+
};
|
|
1710
1721
|
|
|
1711
1722
|
// src/session/list.ts
|
|
1712
1723
|
var FRONT_MATTER_PATTERN = /^---\r?\n([\s\S]*?)\r?\n(?:---|\.\.\.)\r?\n?/;
|
|
@@ -1729,36 +1740,28 @@ function parseSessionMetadata(content) {
|
|
|
1729
1740
|
tags: []
|
|
1730
1741
|
};
|
|
1731
1742
|
}
|
|
1732
|
-
const
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
}
|
|
1737
|
-
const
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
if (typeof
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
metadata.specs = parsed.specs.filter(
|
|
1755
|
-
(s) => typeof s === "string"
|
|
1756
|
-
);
|
|
1757
|
-
}
|
|
1758
|
-
if (Array.isArray(parsed.files)) {
|
|
1759
|
-
metadata.files = parsed.files.filter(
|
|
1760
|
-
(f) => typeof f === "string"
|
|
1761
|
-
);
|
|
1743
|
+
const rawPriority = parsed[SESSION_FRONT_MATTER.PRIORITY];
|
|
1744
|
+
const priority = isValidPriority(rawPriority) ? rawPriority : DEFAULT_PRIORITY;
|
|
1745
|
+
const rawTags = parsed[SESSION_FRONT_MATTER.TAGS];
|
|
1746
|
+
const tags = Array.isArray(rawTags) ? rawTags.filter((t) => typeof t === "string") : [];
|
|
1747
|
+
const metadata = { priority, tags };
|
|
1748
|
+
const id = parsed[SESSION_FRONT_MATTER.ID];
|
|
1749
|
+
if (typeof id === "string") metadata.id = id;
|
|
1750
|
+
const branch = parsed[SESSION_FRONT_MATTER.BRANCH];
|
|
1751
|
+
if (typeof branch === "string") metadata.branch = branch;
|
|
1752
|
+
const createdAt = parsed[SESSION_FRONT_MATTER.CREATED_AT];
|
|
1753
|
+
if (typeof createdAt === "string") metadata.createdAt = createdAt;
|
|
1754
|
+
const agentSessionId = parsed[SESSION_FRONT_MATTER.AGENT_SESSION_ID];
|
|
1755
|
+
if (typeof agentSessionId === "string") metadata.agentSessionId = agentSessionId;
|
|
1756
|
+
const workingDirectory = parsed[SESSION_FRONT_MATTER.WORKING_DIRECTORY];
|
|
1757
|
+
if (typeof workingDirectory === "string") metadata.workingDirectory = workingDirectory;
|
|
1758
|
+
const specs = parsed[SESSION_FRONT_MATTER.SPECS];
|
|
1759
|
+
if (Array.isArray(specs)) {
|
|
1760
|
+
metadata.specs = specs.filter((s) => typeof s === "string");
|
|
1761
|
+
}
|
|
1762
|
+
const files = parsed[SESSION_FRONT_MATTER.FILES];
|
|
1763
|
+
if (Array.isArray(files)) {
|
|
1764
|
+
metadata.files = files.filter((f) => typeof f === "string");
|
|
1762
1765
|
}
|
|
1763
1766
|
return metadata;
|
|
1764
1767
|
} catch {
|
|
@@ -1855,6 +1858,16 @@ import { join as join5, resolve as resolve4 } from "path";
|
|
|
1855
1858
|
|
|
1856
1859
|
// src/session/create.ts
|
|
1857
1860
|
var MIN_CONTENT_LENGTH = 1;
|
|
1861
|
+
var FRONT_MATTER_OPEN = /^---\r?\n/;
|
|
1862
|
+
function preFillSessionContent(content, params) {
|
|
1863
|
+
const match = FRONT_MATTER_OPEN.exec(content);
|
|
1864
|
+
if (!match) return content;
|
|
1865
|
+
const lines = [`${SESSION_FRONT_MATTER.CREATED_AT}: "${params.createdAt.toISOString()}"`];
|
|
1866
|
+
if (params.agentSessionId !== void 0) {
|
|
1867
|
+
lines.push(`${SESSION_FRONT_MATTER.AGENT_SESSION_ID}: "${params.agentSessionId}"`);
|
|
1868
|
+
}
|
|
1869
|
+
return match[0] + lines.join("\n") + "\n" + content.slice(match[0].length);
|
|
1870
|
+
}
|
|
1858
1871
|
function validateSessionContent(content) {
|
|
1859
1872
|
if (!content || content.trim().length < MIN_CONTENT_LENGTH) {
|
|
1860
1873
|
return {
|
|
@@ -1892,7 +1905,12 @@ ${content}`;
|
|
|
1892
1905
|
async function handoffCommand(options) {
|
|
1893
1906
|
const { config, warning } = await resolveSessionConfig({ sessionsDir: options.sessionsDir });
|
|
1894
1907
|
const sessionId = generateSessionId();
|
|
1895
|
-
const
|
|
1908
|
+
const baseContent = buildSessionContent(options.content);
|
|
1909
|
+
const agentSessionId = process.env.CLAUDE_SESSION_ID ?? process.env.CODEX_THREAD_ID;
|
|
1910
|
+
const fullContent = preFillSessionContent(baseContent, {
|
|
1911
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
1912
|
+
agentSessionId
|
|
1913
|
+
});
|
|
1896
1914
|
const validation = validateSessionContent(fullContent);
|
|
1897
1915
|
if (!validation.valid) {
|
|
1898
1916
|
throw new SessionInvalidContentError(validation.error ?? "Unknown validation error");
|
|
@@ -4986,6 +5004,8 @@ function formatSummary(options) {
|
|
|
4986
5004
|
|
|
4987
5005
|
// src/validation/steps/eslint.ts
|
|
4988
5006
|
import { spawn } from "child_process";
|
|
5007
|
+
import { existsSync as existsSync3 } from "fs";
|
|
5008
|
+
import { join as join15 } from "path";
|
|
4989
5009
|
|
|
4990
5010
|
// src/validation/types.ts
|
|
4991
5011
|
var VALIDATION_SCOPES = {
|
|
@@ -5035,7 +5055,10 @@ async function validateESLint(context, runner = defaultEslintProcessRunner) {
|
|
|
5035
5055
|
cacheFile: CACHE_PATHS.ESLINT,
|
|
5036
5056
|
configFile: eslintConfigFile
|
|
5037
5057
|
});
|
|
5038
|
-
const
|
|
5058
|
+
const localBin = join15(projectRoot, "node_modules", ".bin", "eslint");
|
|
5059
|
+
const binary = existsSync3(localBin) ? localBin : "npx";
|
|
5060
|
+
const spawnArgs = binary === "npx" ? eslintArgs : eslintArgs.slice(1);
|
|
5061
|
+
const eslintProcess = runner.spawn(binary, spawnArgs, {
|
|
5039
5062
|
cwd: projectRoot,
|
|
5040
5063
|
stdio: "inherit"
|
|
5041
5064
|
});
|
|
@@ -5064,6 +5087,8 @@ function validationEnabled(envVarKey, defaults2 = {}) {
|
|
|
5064
5087
|
|
|
5065
5088
|
// src/validation/steps/knip.ts
|
|
5066
5089
|
import { spawn as spawn2 } from "child_process";
|
|
5090
|
+
import { existsSync as existsSync4 } from "fs";
|
|
5091
|
+
import { join as join16 } from "path";
|
|
5067
5092
|
var defaultKnipProcessRunner = { spawn: spawn2 };
|
|
5068
5093
|
async function validateKnip(typescriptScope, runner = defaultKnipProcessRunner) {
|
|
5069
5094
|
try {
|
|
@@ -5072,7 +5097,9 @@ async function validateKnip(typescriptScope, runner = defaultKnipProcessRunner)
|
|
|
5072
5097
|
return { success: true };
|
|
5073
5098
|
}
|
|
5074
5099
|
return new Promise((resolve6) => {
|
|
5075
|
-
const
|
|
5100
|
+
const localBin = join16(process.cwd(), "node_modules", ".bin", "knip");
|
|
5101
|
+
const binary = existsSync4(localBin) ? localBin : "npx";
|
|
5102
|
+
const knipProcess = runner.spawn(binary, binary === "npx" ? ["knip"] : [], {
|
|
5076
5103
|
cwd: process.cwd(),
|
|
5077
5104
|
stdio: "pipe"
|
|
5078
5105
|
});
|
|
@@ -5245,8 +5272,8 @@ function formatLoc(loc) {
|
|
|
5245
5272
|
}
|
|
5246
5273
|
|
|
5247
5274
|
// src/validation/steps/markdown.ts
|
|
5248
|
-
import { existsSync as
|
|
5249
|
-
import { basename, join as
|
|
5275
|
+
import { existsSync as existsSync5, readFileSync as readFileSync2 } from "fs";
|
|
5276
|
+
import { basename, join as join17 } from "path";
|
|
5250
5277
|
import { main as markdownlintMain } from "markdownlint-cli2";
|
|
5251
5278
|
import relativeLinksRule from "markdownlint-rule-relative-links";
|
|
5252
5279
|
var DEFAULT_DIRECTORY_NAMES = ["spx", "docs"];
|
|
@@ -5272,11 +5299,11 @@ function buildMarkdownlintConfig(directoryName) {
|
|
|
5272
5299
|
};
|
|
5273
5300
|
}
|
|
5274
5301
|
function getDefaultDirectories(projectRoot) {
|
|
5275
|
-
return DEFAULT_DIRECTORY_NAMES.map((name) =>
|
|
5302
|
+
return DEFAULT_DIRECTORY_NAMES.map((name) => join17(projectRoot, name)).filter((dir) => existsSync5(dir));
|
|
5276
5303
|
}
|
|
5277
5304
|
function getExcludeGlobs(spxDir) {
|
|
5278
|
-
const excludePath =
|
|
5279
|
-
if (!
|
|
5305
|
+
const excludePath = join17(spxDir, EXCLUDE_FILENAME);
|
|
5306
|
+
if (!existsSync5(excludePath)) {
|
|
5280
5307
|
return [];
|
|
5281
5308
|
}
|
|
5282
5309
|
const content = readFileSync2(excludePath, "utf-8");
|
|
@@ -5339,7 +5366,7 @@ async function validateDirectory(directory, config, projectRoot, ignoreGlobs = [
|
|
|
5339
5366
|
if (parsed) {
|
|
5340
5367
|
errors.push({
|
|
5341
5368
|
...parsed,
|
|
5342
|
-
file:
|
|
5369
|
+
file: join17(directory, parsed.file)
|
|
5343
5370
|
});
|
|
5344
5371
|
}
|
|
5345
5372
|
}
|
|
@@ -5384,16 +5411,16 @@ async function markdownCommand(options) {
|
|
|
5384
5411
|
|
|
5385
5412
|
// src/validation/steps/typescript.ts
|
|
5386
5413
|
import { spawn as spawn3 } from "child_process";
|
|
5387
|
-
import { existsSync as
|
|
5414
|
+
import { existsSync as existsSync6, mkdirSync, rmSync, writeFileSync } from "fs";
|
|
5388
5415
|
import { mkdtemp } from "fs/promises";
|
|
5389
5416
|
import { tmpdir } from "os";
|
|
5390
|
-
import { isAbsolute as isAbsolute3, join as
|
|
5417
|
+
import { isAbsolute as isAbsolute3, join as join18 } from "path";
|
|
5391
5418
|
var defaultTypeScriptProcessRunner = { spawn: spawn3 };
|
|
5392
5419
|
var defaultTypeScriptDeps = {
|
|
5393
5420
|
mkdtemp,
|
|
5394
5421
|
writeFileSync,
|
|
5395
5422
|
rmSync,
|
|
5396
|
-
existsSync:
|
|
5423
|
+
existsSync: existsSync6,
|
|
5397
5424
|
mkdirSync
|
|
5398
5425
|
};
|
|
5399
5426
|
function buildTypeScriptArgs(context) {
|
|
@@ -5401,19 +5428,19 @@ function buildTypeScriptArgs(context) {
|
|
|
5401
5428
|
return scope === VALIDATION_SCOPES.FULL ? ["tsc", "--noEmit"] : ["tsc", "--project", configFile];
|
|
5402
5429
|
}
|
|
5403
5430
|
async function createFileSpecificTsconfig(scope, files, deps = defaultTypeScriptDeps) {
|
|
5404
|
-
const tempDir = await deps.mkdtemp(
|
|
5405
|
-
const configPath =
|
|
5431
|
+
const tempDir = await deps.mkdtemp(join18(tmpdir(), "validate-ts-"));
|
|
5432
|
+
const configPath = join18(tempDir, "tsconfig.json");
|
|
5406
5433
|
const baseConfigFile = TSCONFIG_FILES[scope];
|
|
5407
5434
|
const projectRoot = process.cwd();
|
|
5408
|
-
const absoluteFiles = files.map((file) => isAbsolute3(file) ? file :
|
|
5435
|
+
const absoluteFiles = files.map((file) => isAbsolute3(file) ? file : join18(projectRoot, file));
|
|
5409
5436
|
const tempConfig = {
|
|
5410
|
-
extends:
|
|
5437
|
+
extends: join18(projectRoot, baseConfigFile),
|
|
5411
5438
|
files: absoluteFiles,
|
|
5412
5439
|
include: [],
|
|
5413
5440
|
exclude: [],
|
|
5414
5441
|
compilerOptions: {
|
|
5415
5442
|
noEmit: true,
|
|
5416
|
-
typeRoots: [
|
|
5443
|
+
typeRoots: [join18(projectRoot, "node_modules", "@types")],
|
|
5417
5444
|
types: ["node"]
|
|
5418
5445
|
}
|
|
5419
5446
|
};
|
|
@@ -5434,7 +5461,10 @@ async function validateTypeScript(scope, typescriptScope, files, runner = defaul
|
|
|
5434
5461
|
const { configPath, cleanup } = await createFileSpecificTsconfig(scope, files, deps);
|
|
5435
5462
|
try {
|
|
5436
5463
|
return await new Promise((resolve6) => {
|
|
5437
|
-
const
|
|
5464
|
+
const tscBin = join18(process.cwd(), "node_modules", ".bin", "tsc");
|
|
5465
|
+
const tscBinary = existsSync6(tscBin) ? tscBin : "npx";
|
|
5466
|
+
const tscArgs2 = tscBinary === "npx" ? ["tsc", "--project", configPath] : ["--project", configPath];
|
|
5467
|
+
const tscProcess = runner.spawn(tscBinary, tscArgs2, {
|
|
5438
5468
|
cwd: process.cwd(),
|
|
5439
5469
|
stdio: "inherit"
|
|
5440
5470
|
});
|
|
@@ -5457,8 +5487,10 @@ async function validateTypeScript(scope, typescriptScope, files, runner = defaul
|
|
|
5457
5487
|
return { success: false, error: `Failed to create temporary config: ${errorMessage}` };
|
|
5458
5488
|
}
|
|
5459
5489
|
} else {
|
|
5460
|
-
|
|
5461
|
-
|
|
5490
|
+
const tscBin = join18(process.cwd(), "node_modules", ".bin", "tsc");
|
|
5491
|
+
tool = existsSync6(tscBin) ? tscBin : "npx";
|
|
5492
|
+
const rawArgs = buildTypeScriptArgs({ scope, configFile });
|
|
5493
|
+
tscArgs = tool === "npx" ? rawArgs : rawArgs.slice(1);
|
|
5462
5494
|
}
|
|
5463
5495
|
return new Promise((resolve6) => {
|
|
5464
5496
|
const tscProcess = runner.spawn(tool, tscArgs, {
|