@gobing-ai/spur 0.3.83 → 0.3.85
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/.claude-plugin/marketplace.json +1 -1
- package/config/config.global.yaml +11 -6
- package/config/rules/strict/runtime-boundaries.yaml +1 -0
- package/config/workflows/idea-pipeline.yaml +1 -1
- package/config/workflows/wrapup-pipeline.yaml +1 -1
- package/package.json +1 -1
- package/plugins/sp/agents/super-planner.md +2 -1
- package/plugins/sp/lib/idea-handoff.generated.mjs +1 -1
- package/plugins/sp/plugin.json +1 -1
- package/plugins/sp/scripts/surface-drift-inventory.ts +1 -1
- package/plugins/sp/skills/parallel-execution/references/dispatch-surface.md +37 -0
- package/plugins/sp/skills/parallel-execution/references/fan-out-patterns.md +55 -1
- package/plugins/sp/skills/spec-decomposition/references/decomposition.md +12 -0
- package/plugins/sp/skills/spur-dev/references/cross-cutting.md +4 -1
- package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +60 -4
- package/schemas/task-batch.schema.json +5 -0
- package/spur.js +1013 -332
- package/web/_astro/BoardApp.CHenHFia.js +1 -0
- package/web/_astro/{BoardApp.D8bM9pKL.js → BoardApp.yW425dRZ.js} +56 -56
- package/web/_astro/{TaskDetail.BPRqgVUE.js → TaskDetail.CGwJAinW.js} +1 -1
- package/web/_astro/{arc.BPrPES3z.js → arc.B-qNXzSO.js} +1 -1
- package/web/_astro/{architectureDiagram-3BPJPVTR.qX_7q02P.js → architectureDiagram-3BPJPVTR.Bdg-xiji.js} +1 -1
- package/web/_astro/{blockDiagram-GPEHLZMM.CUZfj5V7.js → blockDiagram-GPEHLZMM.DFArg3Kp.js} +1 -1
- package/web/_astro/{c4Diagram-AAUBKEIU.CTaOr8hH.js → c4Diagram-AAUBKEIU.BkOLcjyx.js} +1 -1
- package/web/_astro/channel.CbDHK5UQ.js +1 -0
- package/web/_astro/{chunk-2J33WTMH.Dt-9wf3h.js → chunk-2J33WTMH.DN3c20V4.js} +1 -1
- package/web/_astro/{chunk-4BX2VUAB.CTC2sdoN.js → chunk-4BX2VUAB.B64K0Ttd.js} +1 -1
- package/web/_astro/{chunk-55IACEB6.DQcxt2_g.js → chunk-55IACEB6.C-AJvNwa.js} +1 -1
- package/web/_astro/{chunk-727SXJPM.DXFPSn-a.js → chunk-727SXJPM.DMkr46uS.js} +1 -1
- package/web/_astro/{chunk-AQP2D5EJ.BCx3U4bT.js → chunk-AQP2D5EJ.4_WGbI64.js} +1 -1
- package/web/_astro/{chunk-FMBD7UC4.DL2tJkdO.js → chunk-FMBD7UC4.cbFW_lVE.js} +1 -1
- package/web/_astro/{chunk-ND2GUHAM.DZyflMro.js → chunk-ND2GUHAM.CDW_-2O1.js} +1 -1
- package/web/_astro/{chunk-QZHKN3VN.CUI2mT09.js → chunk-QZHKN3VN.BoYOt_yg.js} +1 -1
- package/web/_astro/{classDiagram-4FO5ZUOK.g4rX4Fr1.js → classDiagram-4FO5ZUOK.BNT6XpHQ.js} +1 -1
- package/web/_astro/{classDiagram-v2-Q7XG4LA2.g4rX4Fr1.js → classDiagram-v2-Q7XG4LA2.BNT6XpHQ.js} +1 -1
- package/web/_astro/{cose-bilkent-S5V4N54A.CzWJLqp0.js → cose-bilkent-S5V4N54A.CI1DQxR3.js} +1 -1
- package/web/_astro/{cynefin-OW5HDTMX.WgsvQeCR.js → cynefin-OW5HDTMX.A8ARH61p.js} +1 -1
- package/web/_astro/{dagre-BM42HDAG.Dzv6ngql.js → dagre-BM42HDAG.u28ZmqwR.js} +1 -1
- package/web/_astro/{diagram-2AECGRRQ.CpJ4a9rU.js → diagram-2AECGRRQ.Dx8JcrGc.js} +1 -1
- package/web/_astro/{diagram-5GNKFQAL.CzPlF_dq.js → diagram-5GNKFQAL.BXJUG9GY.js} +1 -1
- package/web/_astro/{diagram-KO2AKTUF.TAkZNTcQ.js → diagram-KO2AKTUF.BtlMuWqW.js} +1 -1
- package/web/_astro/{diagram-LMA3HP47.uCjoKSag.js → diagram-LMA3HP47.C4T7qaAy.js} +1 -1
- package/web/_astro/{diagram-OG6HWLK6.eMplIjoK.js → diagram-OG6HWLK6.D5HECE10.js} +1 -1
- package/web/_astro/{erDiagram-TEJ5UH35.Bf7zoXGz.js → erDiagram-TEJ5UH35.B_hMR3yw.js} +1 -1
- package/web/_astro/{flowDiagram-I6XJVG4X.B_bHj3gN.js → flowDiagram-I6XJVG4X.BAmpcYl3.js} +1 -1
- package/web/_astro/{ganttDiagram-6RSMTGT7.BasrHRMj.js → ganttDiagram-6RSMTGT7.DV1bSWK-.js} +1 -1
- package/web/_astro/{gitGraphDiagram-PVQCEYII.C6iphq1x.js → gitGraphDiagram-PVQCEYII.DxYKkKrJ.js} +1 -1
- package/web/_astro/index.CcU5weKX.css +1 -0
- package/web/_astro/{infoDiagram-5YYISTIA.HXmDMhW4.js → infoDiagram-5YYISTIA.BAWY2xMb.js} +1 -1
- package/web/_astro/{ishikawaDiagram-YF4QCWOH.BSmW8NiU.js → ishikawaDiagram-YF4QCWOH.CZssRn6V.js} +1 -1
- package/web/_astro/{journeyDiagram-JHISSGLW.DEQow5fo.js → journeyDiagram-JHISSGLW.C--muARd.js} +1 -1
- package/web/_astro/{kanban-definition-UN3LZRKU.IVm9cTdc.js → kanban-definition-UN3LZRKU.D_QCK4et.js} +1 -1
- package/web/_astro/{linear.CrsM73_9.js → linear.DzrmTtZ0.js} +1 -1
- package/web/_astro/{mermaid.core.CfBeDJls.js → mermaid.core.Da03W3iu.js} +4 -4
- package/web/_astro/{mindmap-definition-RKZ34NQL.C3j60Y-0.js → mindmap-definition-RKZ34NQL.6cy-8hR_.js} +1 -1
- package/web/_astro/{pieDiagram-4H26LBE5.B-aCMeEA.js → pieDiagram-4H26LBE5.C5rS1pdU.js} +1 -1
- package/web/_astro/{quadrantDiagram-W4KKPZXB.Cib965yq.js → quadrantDiagram-W4KKPZXB.w56GZZ6Q.js} +1 -1
- package/web/_astro/{requirementDiagram-4Y6WPE33.D61cS4O-.js → requirementDiagram-4Y6WPE33.CipX3Pwu.js} +1 -1
- package/web/_astro/{sankeyDiagram-5OEKKPKP.GKF2qVPy.js → sankeyDiagram-5OEKKPKP.C0VVzgJm.js} +1 -1
- package/web/_astro/{sequenceDiagram-3UESZ5HK.DZnq8F2h.js → sequenceDiagram-3UESZ5HK.BX2dUUbF.js} +1 -1
- package/web/_astro/{stateDiagram-AJRCARHV.DXUFmdgM.js → stateDiagram-AJRCARHV.ypCdgODQ.js} +1 -1
- package/web/_astro/{stateDiagram-v2-BHNVJYJU.BtHmhLEz.js → stateDiagram-v2-BHNVJYJU.In0baEtg.js} +1 -1
- package/web/_astro/{timeline-definition-PNZ67QCA.Cy-WW2ln.js → timeline-definition-PNZ67QCA.CJN4Vkvl.js} +1 -1
- package/web/_astro/{vennDiagram-CIIHVFJN.SLp5b9KI.js → vennDiagram-CIIHVFJN.Cf8KkIPY.js} +1 -1
- package/web/_astro/{wardleyDiagram-YWT4CUSO.Bww45mWV.js → wardleyDiagram-YWT4CUSO.DCZBo9xw.js} +1 -1
- package/web/_astro/{xychartDiagram-2RQKCTM6.DR4swI6a.js → xychartDiagram-2RQKCTM6.Cm4v_MiJ.js} +1 -1
- package/web/index.html +2 -2
- package/web/_astro/BoardApp.D-WlxiN2.js +0 -1
- package/web/_astro/channel.DGZaFHZx.js +0 -1
- package/web/_astro/index.DayyIngm.css +0 -1
package/spur.js
CHANGED
|
@@ -39503,6 +39503,35 @@ var init_projects = __esm(() => {
|
|
|
39503
39503
|
});
|
|
39504
39504
|
});
|
|
39505
39505
|
|
|
39506
|
+
// ../../packages/config/src/slash-commands.ts
|
|
39507
|
+
import { homedir as homedir2 } from "os";
|
|
39508
|
+
import { join as join2 } from "path";
|
|
39509
|
+
function getSlashCommandsFilePath() {
|
|
39510
|
+
if (process.env.SPUR_SLASH_COMMANDS_FILE) {
|
|
39511
|
+
return process.env.SPUR_SLASH_COMMANDS_FILE;
|
|
39512
|
+
}
|
|
39513
|
+
return join2(homedir2(), ".config", "spur", "slash_commands.json");
|
|
39514
|
+
}
|
|
39515
|
+
var slashCommandCategorySchema, slashCommandItemSchema, slashCommandsFileSchema;
|
|
39516
|
+
var init_slash_commands = __esm(() => {
|
|
39517
|
+
init_zod();
|
|
39518
|
+
slashCommandCategorySchema = exports_external.enum(["git", "dev", "sys", "harness"]);
|
|
39519
|
+
slashCommandItemSchema = exports_external.object({
|
|
39520
|
+
name: exports_external.string().min(1),
|
|
39521
|
+
description: exports_external.string().min(1),
|
|
39522
|
+
category: slashCommandCategorySchema.default("dev"),
|
|
39523
|
+
argumentHint: exports_external.string().optional(),
|
|
39524
|
+
tags: exports_external.array(exports_external.string()).default([]),
|
|
39525
|
+
role: exports_external.string().optional(),
|
|
39526
|
+
source: exports_external.string().optional()
|
|
39527
|
+
});
|
|
39528
|
+
slashCommandsFileSchema = exports_external.object({
|
|
39529
|
+
version: exports_external.number().int().default(1),
|
|
39530
|
+
updatedAt: exports_external.string().optional(),
|
|
39531
|
+
commands: exports_external.array(slashCommandItemSchema).default([])
|
|
39532
|
+
});
|
|
39533
|
+
});
|
|
39534
|
+
|
|
39506
39535
|
// ../../packages/config/src/index.ts
|
|
39507
39536
|
function folderPathWithDefault(defaultPath) {
|
|
39508
39537
|
return exports_external.preprocess((v) => v == null ? undefined : v, exports_external.string().default(defaultPath));
|
|
@@ -39608,6 +39637,7 @@ var init_src = __esm(() => {
|
|
|
39608
39637
|
init_zod();
|
|
39609
39638
|
init_finding_codes();
|
|
39610
39639
|
init_projects();
|
|
39640
|
+
init_slash_commands();
|
|
39611
39641
|
SPUR_ENV_VARS = {
|
|
39612
39642
|
nodeEnv: "NODE_ENV",
|
|
39613
39643
|
port: "PORT",
|
|
@@ -39835,14 +39865,14 @@ var init_src = __esm(() => {
|
|
|
39835
39865
|
|
|
39836
39866
|
// ../../packages/config/src/bundled-config.ts
|
|
39837
39867
|
import { existsSync as existsSync3, readdirSync as readdirSync3, statSync as statSync4 } from "fs";
|
|
39838
|
-
import { dirname as dirname3, join as
|
|
39868
|
+
import { dirname as dirname3, join as join3 } from "path";
|
|
39839
39869
|
function bundledConfigRoot() {
|
|
39840
39870
|
if (cachedRoot !== undefined)
|
|
39841
39871
|
return cachedRoot;
|
|
39842
39872
|
let dir = import.meta.dirname;
|
|
39843
39873
|
while (true) {
|
|
39844
39874
|
for (const name of BUNDLED_CONFIG_DIRS) {
|
|
39845
|
-
const candidate =
|
|
39875
|
+
const candidate = join3(dir, name);
|
|
39846
39876
|
if (isBundledConfigDir(candidate)) {
|
|
39847
39877
|
cachedRoot = candidate;
|
|
39848
39878
|
return cachedRoot;
|
|
@@ -39857,7 +39887,7 @@ function bundledConfigRoot() {
|
|
|
39857
39887
|
return cachedRoot;
|
|
39858
39888
|
}
|
|
39859
39889
|
function isBundledConfigDir(candidate) {
|
|
39860
|
-
return existsSync3(candidate) && statSync4(candidate).isDirectory() && existsSync3(
|
|
39890
|
+
return existsSync3(candidate) && statSync4(candidate).isDirectory() && existsSync3(join3(candidate, "rules")) && existsSync3(join3(candidate, "workflows"));
|
|
39861
39891
|
}
|
|
39862
39892
|
function listBundledConfigFiles() {
|
|
39863
39893
|
const root = bundledConfigRoot();
|
|
@@ -39868,7 +39898,7 @@ function listBundledConfigFiles() {
|
|
|
39868
39898
|
function walk(dir, relPrefix, filter = /\.(ya?ml|json)$/i) {
|
|
39869
39899
|
const acc = [];
|
|
39870
39900
|
for (const entry of readdirSync3(dir)) {
|
|
39871
|
-
const abs =
|
|
39901
|
+
const abs = join3(dir, entry);
|
|
39872
39902
|
const rel = relPrefix.length > 0 ? `${relPrefix}/${entry}` : entry;
|
|
39873
39903
|
if (statSync4(abs).isDirectory()) {
|
|
39874
39904
|
acc.push(...walk(abs, rel, filter));
|
|
@@ -39906,7 +39936,7 @@ var init_template_renderer = () => {};
|
|
|
39906
39936
|
|
|
39907
39937
|
// ../../packages/config/src/executor-update.ts
|
|
39908
39938
|
import { chmod, lstat, open, readFile, rename, stat, unlink } from "fs/promises";
|
|
39909
|
-
import { basename, dirname as dirname4, join as
|
|
39939
|
+
import { basename, dirname as dirname4, join as join4 } from "path";
|
|
39910
39940
|
async function setProjectExecutorDisabled(projectRoot, executorName, disabled) {
|
|
39911
39941
|
if (executorName.length === 0) {
|
|
39912
39942
|
throw new ExecutorUpdateError("INVALID_CONFIG", "executor name must be a non-empty string");
|
|
@@ -39914,7 +39944,7 @@ async function setProjectExecutorDisabled(projectRoot, executorName, disabled) {
|
|
|
39914
39944
|
if (typeof disabled !== "boolean") {
|
|
39915
39945
|
throw new ExecutorUpdateError("INVALID_CONFIG", `disabled must be a boolean, received ${typeof disabled}`);
|
|
39916
39946
|
}
|
|
39917
|
-
const configPath =
|
|
39947
|
+
const configPath = join4(projectRoot, ".spur", "config.yaml");
|
|
39918
39948
|
let original;
|
|
39919
39949
|
try {
|
|
39920
39950
|
original = await stat(configPath);
|
|
@@ -39969,7 +39999,7 @@ async function setProjectExecutorDisabled(projectRoot, executorName, disabled) {
|
|
|
39969
39999
|
}
|
|
39970
40000
|
target.set("disabled", disabled);
|
|
39971
40001
|
const serialized = String(doc2);
|
|
39972
|
-
const tmpPath =
|
|
40002
|
+
const tmpPath = join4(dirname4(configPath), `.${basename(configPath)}.tmp-${process.pid}`);
|
|
39973
40003
|
try {
|
|
39974
40004
|
const latest = await stat(configPath);
|
|
39975
40005
|
if (latest.mtimeMs !== baseline.mtimeMs || latest.size !== baseline.size || latest.ino !== baseline.ino) {
|
|
@@ -40057,8 +40087,8 @@ var init_executor_update = __esm(() => {
|
|
|
40057
40087
|
|
|
40058
40088
|
// ../../packages/config/src/loader.ts
|
|
40059
40089
|
import { existsSync as existsSync4, statSync as statSync5 } from "fs";
|
|
40060
|
-
import { homedir as
|
|
40061
|
-
import { dirname as dirname5, join as
|
|
40090
|
+
import { homedir as homedir3 } from "os";
|
|
40091
|
+
import { dirname as dirname5, join as join5, resolve } from "path";
|
|
40062
40092
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
40063
40093
|
function embeddedSchemasId(embeddedSchemas) {
|
|
40064
40094
|
if (embeddedSchemas === undefined)
|
|
@@ -40081,7 +40111,7 @@ function cacheKey(configPath, opts, validateJsonSchema2) {
|
|
|
40081
40111
|
}
|
|
40082
40112
|
function resolveSchemaSpecifier(specifier, manifestSpecifier) {
|
|
40083
40113
|
if (specifier === manifestSpecifier) {
|
|
40084
|
-
const workspaceManifest =
|
|
40114
|
+
const workspaceManifest = join5(LOADER_DIR, "..", "..", "..", "apps", "cli", "package.json");
|
|
40085
40115
|
if (existsSync4(workspaceManifest))
|
|
40086
40116
|
return workspaceManifest;
|
|
40087
40117
|
}
|
|
@@ -40096,7 +40126,7 @@ function resolveConfigLayers(cwd) {
|
|
|
40096
40126
|
const layers = {};
|
|
40097
40127
|
const skipProject = cwd === undefined && process.env.SPUR_SKIP_PROJECT_CONFIG === "true";
|
|
40098
40128
|
if (!skipProject) {
|
|
40099
|
-
const projectConfig =
|
|
40129
|
+
const projectConfig = join5(cwd ?? process.cwd(), SPUR_CONFIG_DIR, SPUR_CONFIG_FILE);
|
|
40100
40130
|
if (existsSync4(projectConfig))
|
|
40101
40131
|
layers.project = projectConfig;
|
|
40102
40132
|
}
|
|
@@ -40169,9 +40199,9 @@ function assertNoGlobalFleetSection(globalRaw, layerPath, projectFile) {
|
|
|
40169
40199
|
throw new Error(`agent.fleet is not supported in the global config (${layerPath ?? SPUR_CONFIG_FILE}). ` + `Declare the project fleet under agent.fleet in ${projectFile} \u2014 the flock belongs to a project, not to the machine.`);
|
|
40170
40200
|
}
|
|
40171
40201
|
function assertNoLegacyFleetFile(projectRoot, projectConfigPath) {
|
|
40172
|
-
const candidates = new Set([
|
|
40202
|
+
const candidates = new Set([join5(projectRoot, SPUR_CONFIG_DIR, "fleet.json")]);
|
|
40173
40203
|
if (projectConfigPath !== undefined)
|
|
40174
|
-
candidates.add(
|
|
40204
|
+
candidates.add(join5(dirname5(projectConfigPath), "fleet.json"));
|
|
40175
40205
|
for (const legacyFile of candidates) {
|
|
40176
40206
|
if (!existsSync4(legacyFile))
|
|
40177
40207
|
continue;
|
|
@@ -40179,7 +40209,7 @@ function assertNoLegacyFleetFile(projectRoot, projectConfigPath) {
|
|
|
40179
40209
|
}
|
|
40180
40210
|
}
|
|
40181
40211
|
function projectConfigReplacement(layers) {
|
|
40182
|
-
return layers.project ??
|
|
40212
|
+
return layers.project ?? join5(SPUR_CONFIG_DIR, SPUR_CONFIG_FILE);
|
|
40183
40213
|
}
|
|
40184
40214
|
function schemaValidationContext(opts) {
|
|
40185
40215
|
const embeddedSchemas = opts?.embeddedSchemas;
|
|
@@ -40434,7 +40464,7 @@ async function resolvePlanningFolders(fs2) {
|
|
|
40434
40464
|
return promise2;
|
|
40435
40465
|
}
|
|
40436
40466
|
async function resolvePlanningFoldersUncached(fs2) {
|
|
40437
|
-
const configPath = fs2.resolve(
|
|
40467
|
+
const configPath = fs2.resolve(join5(SPUR_CONFIG_DIR, SPUR_CONFIG_FILE));
|
|
40438
40468
|
if (!await fs2.exists(configPath))
|
|
40439
40469
|
return defaultPlanningFolders();
|
|
40440
40470
|
try {
|
|
@@ -40473,7 +40503,7 @@ var init_loader = __esm(() => {
|
|
|
40473
40503
|
init_template_renderer();
|
|
40474
40504
|
init_executor_update();
|
|
40475
40505
|
init_projects();
|
|
40476
|
-
GLOBAL_CONFIG_FILE =
|
|
40506
|
+
GLOBAL_CONFIG_FILE = join5(homedir3(), ".config", "spur", "config.yaml");
|
|
40477
40507
|
LOADER_DIR = dirname5(fileURLToPath3(import.meta.url));
|
|
40478
40508
|
embeddedSchemasIds = new WeakMap;
|
|
40479
40509
|
spurConfigCache = new Map;
|
|
@@ -68003,7 +68033,7 @@ var init_schema3 = __esm(() => {
|
|
|
68003
68033
|
});
|
|
68004
68034
|
|
|
68005
68035
|
// ../../packages/domain/src/migrations.ts
|
|
68006
|
-
import { join as
|
|
68036
|
+
import { join as join7 } from "path";
|
|
68007
68037
|
async function allMigrationsJournaled(adapter, migrations) {
|
|
68008
68038
|
if (migrations.length === 0)
|
|
68009
68039
|
return true;
|
|
@@ -68116,7 +68146,7 @@ async function loadSqlMigrations(folder) {
|
|
|
68116
68146
|
for (const entry of entries) {
|
|
68117
68147
|
migrations.push({
|
|
68118
68148
|
id: entry.replace(/\.sql$/, ""),
|
|
68119
|
-
sql: await fs3.readFile(
|
|
68149
|
+
sql: await fs3.readFile(join7(folder, entry))
|
|
68120
68150
|
});
|
|
68121
68151
|
}
|
|
68122
68152
|
return migrations.length > 0 ? migrations : CLI_MIGRATIONS;
|
|
@@ -70803,7 +70833,7 @@ var init_envelope = __esm(() => {
|
|
|
70803
70833
|
|
|
70804
70834
|
// ../../packages/domain/src/retention.ts
|
|
70805
70835
|
import { readdirSync as readdirSync5, rmSync as rmSync3, statfsSync, statSync as statSync6 } from "fs";
|
|
70806
|
-
import { join as
|
|
70836
|
+
import { join as join8 } from "path";
|
|
70807
70837
|
async function estimateReclaimableBytes(db2) {
|
|
70808
70838
|
try {
|
|
70809
70839
|
const rows = await db2.queryAll("SELECT pageno, pagetype, ncell, payload, unused FROM dbstat ORDER BY pageno");
|
|
@@ -70871,7 +70901,7 @@ async function runRetention(db2, cwd, now = new Date) {
|
|
|
70871
70901
|
result.queueJobs = await purgeQueueJobs(db2, now);
|
|
70872
70902
|
result.ledgerRows = await purgeLedger(db2, now);
|
|
70873
70903
|
result.backupFiles = pruneBackups(cwd, now);
|
|
70874
|
-
result.compaction = await compactDatabase(db2, { dbPath:
|
|
70904
|
+
result.compaction = await compactDatabase(db2, { dbPath: join8(cwd, ".spur", "spur.db"), now });
|
|
70875
70905
|
return result;
|
|
70876
70906
|
}
|
|
70877
70907
|
async function purgeRuleEvalRuns(db2, now) {
|
|
@@ -70884,7 +70914,7 @@ async function purgeLedger(db2, now) {
|
|
|
70884
70914
|
return purgeRows(db2, `DELETE FROM history_import_ledger WHERE imported_at < ?`, new Date(now.getTime() - LEDGER_RETENTION_DAYS * DAY_MS).toISOString());
|
|
70885
70915
|
}
|
|
70886
70916
|
function pruneBackups(cwd, now) {
|
|
70887
|
-
const dir =
|
|
70917
|
+
const dir = join8(cwd, ".spur", "backups");
|
|
70888
70918
|
let entries;
|
|
70889
70919
|
try {
|
|
70890
70920
|
entries = readdirSync5(dir);
|
|
@@ -70894,7 +70924,7 @@ function pruneBackups(cwd, now) {
|
|
|
70894
70924
|
const cutoffMs = now.getTime() - BACKUP_RETENTION_DAYS * DAY_MS;
|
|
70895
70925
|
let reclaimed = 0;
|
|
70896
70926
|
for (const name of entries) {
|
|
70897
|
-
const path9 =
|
|
70927
|
+
const path9 = join8(dir, name);
|
|
70898
70928
|
try {
|
|
70899
70929
|
const stat2 = statSync6(path9);
|
|
70900
70930
|
if (!stat2.isFile() || stat2.mtimeMs >= cutoffMs)
|
|
@@ -70979,12 +71009,12 @@ var init_maintenance = __esm(() => {
|
|
|
70979
71009
|
|
|
70980
71010
|
// ../../packages/domain/src/planning/locks.ts
|
|
70981
71011
|
import { closeSync, fsyncSync, mkdirSync as mkdirSync4, openSync, readFileSync as readFileSync7, renameSync as renameSync3, unlinkSync, writeSync } from "fs";
|
|
70982
|
-
import { dirname as dirname8, join as
|
|
71012
|
+
import { dirname as dirname8, join as join9 } from "path";
|
|
70983
71013
|
function lockPathForEntity(folder, entityId) {
|
|
70984
|
-
return
|
|
71014
|
+
return join9(folder, `.${entityId}.lock`);
|
|
70985
71015
|
}
|
|
70986
71016
|
function createLockPath(folder) {
|
|
70987
|
-
return
|
|
71017
|
+
return join9(folder, ".create.lock");
|
|
70988
71018
|
}
|
|
70989
71019
|
function parseLockContent(raw) {
|
|
70990
71020
|
const str = typeof raw === "string" ? raw : raw.toString();
|
|
@@ -71089,7 +71119,7 @@ function fsyncPath(path9, swallowErrors = false) {
|
|
|
71089
71119
|
function atomicWrite(dest, content, entityId, fs3, projectName = "spur") {
|
|
71090
71120
|
const dir = dirname8(dest);
|
|
71091
71121
|
const rand = Math.random().toString(36).slice(2, 10);
|
|
71092
|
-
const tempPath =
|
|
71122
|
+
const tempPath = join9(dir, `.${projectName}.${entityId}.${rand}.tmp`);
|
|
71093
71123
|
fs3.writeFile(tempPath, content);
|
|
71094
71124
|
fsyncPath(tempPath);
|
|
71095
71125
|
renameSync3(tempPath, dest);
|
|
@@ -71098,7 +71128,7 @@ function atomicWrite(dest, content, entityId, fs3, projectName = "spur") {
|
|
|
71098
71128
|
async function atomicWriteAsync(dest, content, entityId, fs3, projectName = "spur") {
|
|
71099
71129
|
const dir = dirname8(dest);
|
|
71100
71130
|
const rand = Math.random().toString(36).slice(2, 10);
|
|
71101
|
-
const tempPath =
|
|
71131
|
+
const tempPath = join9(dir, `.${projectName}.${entityId}.${rand}.tmp`);
|
|
71102
71132
|
await fs3.writeFile(tempPath, content);
|
|
71103
71133
|
fsyncPath(tempPath);
|
|
71104
71134
|
renameSync3(tempPath, dest);
|
|
@@ -71113,7 +71143,7 @@ function cleanupTempFiles(folder, entityId, fs3, projectName = "spur") {
|
|
|
71113
71143
|
let count3 = 0;
|
|
71114
71144
|
for (const entry of entries) {
|
|
71115
71145
|
if (entry.startsWith(prefix) && entry.endsWith(suffix)) {
|
|
71116
|
-
fs3.deleteFile(
|
|
71146
|
+
fs3.deleteFile(join9(folder, entry));
|
|
71117
71147
|
count3++;
|
|
71118
71148
|
}
|
|
71119
71149
|
}
|
|
@@ -71694,6 +71724,7 @@ var init_schema6 = __esm(() => {
|
|
|
71694
71724
|
done_forced: exports_external.preprocess((v) => typeof v === "string" ? v === "true" : v, exports_external.boolean().optional()),
|
|
71695
71725
|
done_reason: exports_external.string().optional(),
|
|
71696
71726
|
feature_link_declined: exports_external.preprocess((v) => typeof v === "string" ? v === "true" : v, exports_external.boolean().optional()),
|
|
71727
|
+
estimate_hours: exports_external.preprocess((v) => typeof v === "string" && v.trim() !== "" ? Number(v) : v, exports_external.number().positive().optional()),
|
|
71697
71728
|
created_at: isoDateString,
|
|
71698
71729
|
updated_at: isoDateString
|
|
71699
71730
|
});
|
|
@@ -71721,7 +71752,8 @@ var init_schema6 = __esm(() => {
|
|
|
71721
71752
|
parent_wbs: wbsString,
|
|
71722
71753
|
priority: exports_external.enum(PRIORITIES).optional(),
|
|
71723
71754
|
tags: exports_external.array(exports_external.string()).optional(),
|
|
71724
|
-
template: exports_external.enum(TASK_VARIANTS).optional()
|
|
71755
|
+
template: exports_external.enum(TASK_VARIANTS).optional(),
|
|
71756
|
+
estimate_hours: exports_external.number().positive().optional()
|
|
71725
71757
|
}).strict();
|
|
71726
71758
|
taskBatchSchema = exports_external.array(taskBatchItemSchema).min(1, "batch must contain at least one task");
|
|
71727
71759
|
});
|
|
@@ -73046,7 +73078,7 @@ var init_escalation_packet = __esm(() => {
|
|
|
73046
73078
|
|
|
73047
73079
|
// ../../packages/app/src/observability/escalation-packet-sink.ts
|
|
73048
73080
|
import { randomUUID } from "crypto";
|
|
73049
|
-
import { join as
|
|
73081
|
+
import { join as join10 } from "path";
|
|
73050
73082
|
function parseDryRunProbeMetadata(metadataJson) {
|
|
73051
73083
|
if (metadataJson === "")
|
|
73052
73084
|
return false;
|
|
@@ -73093,7 +73125,7 @@ class EscalationPacketSink {
|
|
|
73093
73125
|
}
|
|
73094
73126
|
}
|
|
73095
73127
|
async readPacket(runId) {
|
|
73096
|
-
const path9 =
|
|
73128
|
+
const path9 = join10(this.options.cwd, ".spur", "run", `${runId}-escalation.json`);
|
|
73097
73129
|
if (!await this.options.fs.exists(path9))
|
|
73098
73130
|
return;
|
|
73099
73131
|
return JSON.parse(await this.options.fs.readFile(path9));
|
|
@@ -73161,7 +73193,7 @@ class EscalationPacketSink {
|
|
|
73161
73193
|
const identity6 = await this.resolveIdentity(wbs);
|
|
73162
73194
|
const now2 = (this.options.now ?? (() => new Date().toISOString()))();
|
|
73163
73195
|
const packet = buildEscalationPacket({ ...packetInput, identity: identity6, now: now2 });
|
|
73164
|
-
const path9 =
|
|
73196
|
+
const path9 = join10(this.options.cwd, ".spur", "run", `${input.runId}-escalation.json`);
|
|
73165
73197
|
if (!await this.options.fs.exists(path9)) {
|
|
73166
73198
|
await this.options.fs.writeFile(path9, `${JSON.stringify(packet, null, 2)}
|
|
73167
73199
|
`);
|
|
@@ -73452,7 +73484,7 @@ var init_step_reporter = () => {};
|
|
|
73452
73484
|
|
|
73453
73485
|
// ../../packages/app/src/observability/workflow-run-log-sink.ts
|
|
73454
73486
|
import { closeSync as closeSync2, mkdirSync as mkdirSync5, openSync as openSync2, writeSync as writeSync2 } from "fs";
|
|
73455
|
-
import { join as
|
|
73487
|
+
import { join as join11 } from "path";
|
|
73456
73488
|
|
|
73457
73489
|
class WorkflowRunLogSink {
|
|
73458
73490
|
filePath;
|
|
@@ -73468,7 +73500,7 @@ class WorkflowRunLogSink {
|
|
|
73468
73500
|
bus;
|
|
73469
73501
|
handlers;
|
|
73470
73502
|
constructor(options) {
|
|
73471
|
-
this.filePath =
|
|
73503
|
+
this.filePath = join11(options.dir, `${options.runId}.log`);
|
|
73472
73504
|
this.maxBytes = options.maxBytes ?? DEFAULT_RUN_LOG_MAX_BYTES;
|
|
73473
73505
|
this.maxLines = options.maxLines;
|
|
73474
73506
|
this.planPreview = options.planPreview;
|
|
@@ -73923,7 +73955,7 @@ var init_failure_classification = __esm(() => {
|
|
|
73923
73955
|
});
|
|
73924
73956
|
|
|
73925
73957
|
// ../../packages/app/src/services/run-session-observer.ts
|
|
73926
|
-
import { homedir as
|
|
73958
|
+
import { homedir as homedir4 } from "os";
|
|
73927
73959
|
import { basename as basename2, isAbsolute as isAbsolute2, resolve as resolve3 } from "path";
|
|
73928
73960
|
|
|
73929
73961
|
class RunSessionObserver {
|
|
@@ -73935,7 +73967,7 @@ class RunSessionObserver {
|
|
|
73935
73967
|
now;
|
|
73936
73968
|
constructor(options) {
|
|
73937
73969
|
this.options = options;
|
|
73938
|
-
this.home = options.home ??
|
|
73970
|
+
this.home = options.home ?? homedir4();
|
|
73939
73971
|
this.cwd = options.cwd ?? process.cwd();
|
|
73940
73972
|
this.now = options.now ?? Date.now;
|
|
73941
73973
|
}
|
|
@@ -74147,7 +74179,7 @@ var init_run_session_observer = __esm(() => {
|
|
|
74147
74179
|
|
|
74148
74180
|
// ../../packages/app/src/services/agent-service.ts
|
|
74149
74181
|
import { createHash as createHash8 } from "crypto";
|
|
74150
|
-
import { homedir as
|
|
74182
|
+
import { homedir as homedir5 } from "os";
|
|
74151
74183
|
import { isatty as isatty3 } from "tty";
|
|
74152
74184
|
|
|
74153
74185
|
class AgentService {
|
|
@@ -74565,7 +74597,7 @@ class AgentService {
|
|
|
74565
74597
|
getDb,
|
|
74566
74598
|
output: this.ctx.output,
|
|
74567
74599
|
registry: this.sessionRootRegistry,
|
|
74568
|
-
home:
|
|
74600
|
+
home: homedir5(),
|
|
74569
74601
|
cwd: cwd || this.ctx.cwd,
|
|
74570
74602
|
json: jsonOutput
|
|
74571
74603
|
}));
|
|
@@ -75669,15 +75701,15 @@ var init_agent_service = __esm(() => {
|
|
|
75669
75701
|
// ../../packages/app/src/services/project-registry.ts
|
|
75670
75702
|
import { existsSync as existsSync5, mkdirSync as mkdirSync6, readFileSync as readFileSync8, realpathSync as realpathSync3, renameSync as renameSync4, rmSync as rmSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
75671
75703
|
import { connect, createServer } from "net";
|
|
75672
|
-
import { homedir as
|
|
75673
|
-
import { dirname as dirname9, join as
|
|
75704
|
+
import { homedir as homedir6 } from "os";
|
|
75705
|
+
import { dirname as dirname9, join as join12, resolve as resolve4 } from "path";
|
|
75674
75706
|
function normalizeProjectPath(pathInput) {
|
|
75675
75707
|
const trimmed = pathInput.trim();
|
|
75676
75708
|
let expanded = trimmed;
|
|
75677
75709
|
if (trimmed === "~") {
|
|
75678
|
-
expanded =
|
|
75710
|
+
expanded = homedir6();
|
|
75679
75711
|
} else if (trimmed.startsWith("~/")) {
|
|
75680
|
-
expanded =
|
|
75712
|
+
expanded = join12(homedir6(), trimmed.slice(2));
|
|
75681
75713
|
} else {
|
|
75682
75714
|
expanded = resolve4(trimmed);
|
|
75683
75715
|
}
|
|
@@ -75978,7 +76010,7 @@ var init_project_registry = __esm(() => {
|
|
|
75978
76010
|
});
|
|
75979
76011
|
|
|
75980
76012
|
// ../../packages/app/src/services/fleet-service.ts
|
|
75981
|
-
import { basename as basename3, dirname as dirname10, join as
|
|
76013
|
+
import { basename as basename3, dirname as dirname10, join as join13 } from "path";
|
|
75982
76014
|
function resolveMemberExecutor(params) {
|
|
75983
76015
|
const { member, index: index2, label, agentConfig, roles, roster } = params;
|
|
75984
76016
|
if (member.executor !== undefined) {
|
|
@@ -76188,7 +76220,7 @@ class FleetService {
|
|
|
76188
76220
|
}
|
|
76189
76221
|
const slug = await this.projectSlug(normalized);
|
|
76190
76222
|
const config4 = await this.effectiveConfig();
|
|
76191
|
-
const configDir =
|
|
76223
|
+
const configDir = join13(normalized, ".spur", "agents");
|
|
76192
76224
|
const specs = await loadAgentSpecs(configDir);
|
|
76193
76225
|
const projection = materializeRoster({
|
|
76194
76226
|
slug,
|
|
@@ -76242,7 +76274,7 @@ class FleetService {
|
|
|
76242
76274
|
}
|
|
76243
76275
|
async assertLaunchGroundTruth(projectPath) {
|
|
76244
76276
|
const expected = normalizeProjectPath(projectPath);
|
|
76245
|
-
const expectedStorageRoot =
|
|
76277
|
+
const expectedStorageRoot = join13(expected, ".spur");
|
|
76246
76278
|
const cwd = normalizeProjectPath(process.cwd());
|
|
76247
76279
|
const storageRoot = normalizeProjectPath(this.fs.resolve(".spur"));
|
|
76248
76280
|
const databasePath = this.ctx.openDb ? await new ProjectClaimDao(await this.ctx.openDb(expected)).databasePath() : "";
|
|
@@ -76350,7 +76382,7 @@ var init_task_locator = __esm(() => {
|
|
|
76350
76382
|
});
|
|
76351
76383
|
|
|
76352
76384
|
// ../../packages/app/src/services/agent-coordination-service.ts
|
|
76353
|
-
import { join as
|
|
76385
|
+
import { join as join14 } from "path";
|
|
76354
76386
|
|
|
76355
76387
|
class AgentCoordinationService {
|
|
76356
76388
|
ctx;
|
|
@@ -76358,7 +76390,7 @@ class AgentCoordinationService {
|
|
|
76358
76390
|
orchestratorPromise;
|
|
76359
76391
|
constructor(ctx) {
|
|
76360
76392
|
this.ctx = ctx;
|
|
76361
|
-
this.configDir =
|
|
76393
|
+
this.configDir = join14(ctx.cwd, ".spur", "agents");
|
|
76362
76394
|
}
|
|
76363
76395
|
async sendMessage(fromId, toId, body, replyTo, requestKey) {
|
|
76364
76396
|
validateAgentId(toId);
|
|
@@ -76624,7 +76656,7 @@ class AgentCoordinationService {
|
|
|
76624
76656
|
async resolveTaskFile(taskId) {
|
|
76625
76657
|
const fs3 = this.ctx.fs;
|
|
76626
76658
|
const { foldersConfig } = await resolvePlanningFolders(fs3);
|
|
76627
|
-
const dirs = [...new Set([foldersConfig.active_folder, ...Object.keys(foldersConfig.folders)])].map((dir) =>
|
|
76659
|
+
const dirs = [...new Set([foldersConfig.active_folder, ...Object.keys(foldersConfig.folders)])].map((dir) => join14(this.ctx.cwd, dir));
|
|
76628
76660
|
return await TaskLocator.forDirs(fs3, dirs).findPathByWbs(taskId);
|
|
76629
76661
|
}
|
|
76630
76662
|
}
|
|
@@ -76976,7 +77008,7 @@ var init_agent_quota_updates = __esm(() => {
|
|
|
76976
77008
|
});
|
|
76977
77009
|
|
|
76978
77010
|
// ../../packages/app/src/services/anchor-qualifier.ts
|
|
76979
|
-
import { basename as basename4, join as
|
|
77011
|
+
import { basename as basename4, join as join15 } from "path";
|
|
76980
77012
|
async function resolveRepoRoot(projectRoot, hintDir) {
|
|
76981
77013
|
if (projectRoot)
|
|
76982
77014
|
return projectRoot;
|
|
@@ -77105,7 +77137,7 @@ async function qualifyAnchors(fs3, opts) {
|
|
|
77105
77137
|
} catch {
|
|
77106
77138
|
continue;
|
|
77107
77139
|
}
|
|
77108
|
-
const mdFiles = entries.filter((name) => name.endsWith(".md") && name !== "kanban.md").map((name) =>
|
|
77140
|
+
const mdFiles = entries.filter((name) => name.endsWith(".md") && name !== "kanban.md").map((name) => join15(dir, name)).sort();
|
|
77109
77141
|
for (const filePath of mdFiles) {
|
|
77110
77142
|
let raw;
|
|
77111
77143
|
try {
|
|
@@ -78327,7 +78359,7 @@ __export(exports_feature_check, {
|
|
|
78327
78359
|
FeatureCheckService: () => FeatureCheckService,
|
|
78328
78360
|
DEFAULT_FEATURE_MATRIX: () => DEFAULT_FEATURE_MATRIX
|
|
78329
78361
|
});
|
|
78330
|
-
import { dirname as dirname11, join as
|
|
78362
|
+
import { dirname as dirname11, join as join16 } from "path";
|
|
78331
78363
|
function isGroupFeature(fm) {
|
|
78332
78364
|
const tags = fm.tags;
|
|
78333
78365
|
return Array.isArray(tags) && tags.includes("group") ? "group" : "standard";
|
|
@@ -78425,9 +78457,9 @@ function decodeVerdictRows(source, sectionName, required2) {
|
|
|
78425
78457
|
function defaultVerdictRunDir(tasksDir) {
|
|
78426
78458
|
const norm = tasksDir.replace(/\\/g, "/");
|
|
78427
78459
|
if (/\/docs\/tasks\d*$/.test(norm) || /\/docs\/tasks$/.test(norm)) {
|
|
78428
|
-
return
|
|
78460
|
+
return join16(dirname11(dirname11(tasksDir)), ".spur", "run");
|
|
78429
78461
|
}
|
|
78430
|
-
return
|
|
78462
|
+
return join16(dirname11(tasksDir), ".spur", "run");
|
|
78431
78463
|
}
|
|
78432
78464
|
function rowMatchesScenario(id, sc) {
|
|
78433
78465
|
const stripped = id.replace(/^\[[^\]]*\]\s*/, "").replace(/^Scenario:\s*/i, "").replace(/^\[[^\]]*\]\s*/, "").trim();
|
|
@@ -78557,7 +78589,7 @@ var init_feature_check = __esm(() => {
|
|
|
78557
78589
|
await this.checkOneActiveGoal(fm, featureId2, options.featuresDir, findings, options.asStatus);
|
|
78558
78590
|
await this.checkChildrenLimit(featureId2, options.featuresDir, findings);
|
|
78559
78591
|
}
|
|
78560
|
-
const dogfoodDir = options?.dogfoodDir ?? (options?.featuresDir ?
|
|
78592
|
+
const dogfoodDir = options?.dogfoodDir ?? (options?.featuresDir ? join16(dirname11(options.featuresDir), "dogfood") : undefined);
|
|
78561
78593
|
const primaryTasksDir = options?.tasksDir ?? options?.tasksDirs?.[0];
|
|
78562
78594
|
const runDir = options?.runDir ?? (primaryTasksDir ? defaultVerdictRunDir(primaryTasksDir) : undefined);
|
|
78563
78595
|
const taskScanDirs = options?.tasksDirs && options.tasksDirs.length > 0 ? options.tasksDirs : options?.tasksDir ? [options.tasksDir] : [];
|
|
@@ -78988,7 +79020,7 @@ var init_feature_check = __esm(() => {
|
|
|
78988
79020
|
});
|
|
78989
79021
|
|
|
78990
79022
|
// ../../packages/app/src/services/task-check.ts
|
|
78991
|
-
import { basename as basename5, dirname as dirname12, join as
|
|
79023
|
+
import { basename as basename5, dirname as dirname12, join as join17 } from "path";
|
|
78992
79024
|
function isPlaceholderBody(body) {
|
|
78993
79025
|
const stripped = body.replace(/<!--[\s\S]*?-->/g, "").replace(/^\s*>\s*TBD\s*$/gim, "").trim();
|
|
78994
79026
|
return stripped.length === 0;
|
|
@@ -79248,7 +79280,7 @@ var init_task_check = __esm(() => {
|
|
|
79248
79280
|
this.runL2(doc2, entry, findings, raw);
|
|
79249
79281
|
this.runL3(doc2, entry, effectiveStatus, findings, isTransitionTarget);
|
|
79250
79282
|
const tasksDir = dirname12(filePath);
|
|
79251
|
-
const featuresDir =
|
|
79283
|
+
const featuresDir = join17(dirname12(tasksDir), "features");
|
|
79252
79284
|
await this.runL4(doc2, fm, effectiveStatus, findings, featuresDir, tasksDir, wbs);
|
|
79253
79285
|
await this.runL4Rollup(doc2, wbs, effectiveStatus, findings, tasksDir);
|
|
79254
79286
|
if (effectiveStatus !== "done" && effectiveStatus !== "cancelled") {
|
|
@@ -79815,7 +79847,7 @@ var init_task_check = __esm(() => {
|
|
|
79815
79847
|
for (const cite of citations) {
|
|
79816
79848
|
if (reported >= 5)
|
|
79817
79849
|
break;
|
|
79818
|
-
const abs =
|
|
79850
|
+
const abs = join17(projectRoot, cite.path);
|
|
79819
79851
|
let exists2 = false;
|
|
79820
79852
|
try {
|
|
79821
79853
|
exists2 = await this.fs.exists(abs);
|
|
@@ -79899,7 +79931,7 @@ var init_task_check = __esm(() => {
|
|
|
79899
79931
|
continue;
|
|
79900
79932
|
}
|
|
79901
79933
|
try {
|
|
79902
|
-
const abs =
|
|
79934
|
+
const abs = join17(dir, name);
|
|
79903
79935
|
const stat2 = await this.fs.stat(abs);
|
|
79904
79936
|
if (stat2 !== null && stat2 !== undefined && stat2.isDirectory?.())
|
|
79905
79937
|
stack.push(abs);
|
|
@@ -79994,7 +80026,7 @@ var init_task_check = __esm(() => {
|
|
|
79994
80026
|
}
|
|
79995
80027
|
async checkVerdictArtifact(wbs, tasksDir, findings) {
|
|
79996
80028
|
const projectRoot = resolveProjectRootFromTasksDir(tasksDir);
|
|
79997
|
-
const runDir =
|
|
80029
|
+
const runDir = join17(projectRoot, ".spur", "run");
|
|
79998
80030
|
const verdictPath = `${runDir}/${wbs}-verdict.json`;
|
|
79999
80031
|
const outcome = await readVerifyVerdict(this.fs, verdictPath, wbs);
|
|
80000
80032
|
if (outcome.kind === "missing")
|
|
@@ -80039,13 +80071,13 @@ var init_task_check = __esm(() => {
|
|
|
80039
80071
|
});
|
|
80040
80072
|
|
|
80041
80073
|
// ../../packages/app/src/services/corpus-check.ts
|
|
80042
|
-
import { basename as basename6, dirname as dirname13, join as
|
|
80074
|
+
import { basename as basename6, dirname as dirname13, join as join18, relative, resolve as resolve5 } from "path";
|
|
80043
80075
|
async function loadTaskMatrix(projectRoot) {
|
|
80044
80076
|
const fs3 = createNodeFileSystem3(projectRoot);
|
|
80045
|
-
const candidates = [
|
|
80077
|
+
const candidates = [join18(projectRoot, ".spur", "tasks", "section-matrix.yaml")];
|
|
80046
80078
|
const bundledRoot = bundledConfigRoot();
|
|
80047
80079
|
if (bundledRoot !== null)
|
|
80048
|
-
candidates.push(
|
|
80080
|
+
candidates.push(join18(bundledRoot, "tasks", "section-matrix.yaml"));
|
|
80049
80081
|
let matrixPath;
|
|
80050
80082
|
for (const candidate of candidates) {
|
|
80051
80083
|
if (await fs3.exists(candidate)) {
|
|
@@ -80088,7 +80120,7 @@ async function structuralSweep(projectRoot) {
|
|
|
80088
80120
|
const wbs = fileName.match(/^(\d{4})_.+\.md$/)?.[1];
|
|
80089
80121
|
if (wbs === undefined)
|
|
80090
80122
|
continue;
|
|
80091
|
-
const result = await taskService.check(
|
|
80123
|
+
const result = await taskService.check(join18(tasksDir, fileName), wbs);
|
|
80092
80124
|
for (const finding of result.findings) {
|
|
80093
80125
|
findings.push({
|
|
80094
80126
|
kind: "task",
|
|
@@ -80106,7 +80138,7 @@ async function structuralSweep(projectRoot) {
|
|
|
80106
80138
|
const id = fileName.match(/^([A-Z][0-9]*)_.+\.md$/)?.[1];
|
|
80107
80139
|
if (id === undefined)
|
|
80108
80140
|
continue;
|
|
80109
|
-
const result = await featureService.check(
|
|
80141
|
+
const result = await featureService.check(join18(featuresDir, fileName), id, {
|
|
80110
80142
|
featuresDir,
|
|
80111
80143
|
tasksDir: activeTasksDir,
|
|
80112
80144
|
tasksDirs: taskDirs,
|
|
@@ -80135,7 +80167,7 @@ async function duplicateIds(cwd, taskDirs, featuresDir) {
|
|
|
80135
80167
|
return [];
|
|
80136
80168
|
}
|
|
80137
80169
|
const pattern = kind === "task" ? /^(\d{4})_/ : /^([A-Z][0-9]*)_/;
|
|
80138
|
-
return names.map((n2) => ({ m: n2.match(pattern), n: n2 })).filter((x) => x.m !== null).map((x) => ({ id: x.m[1], file: relative(cwd,
|
|
80170
|
+
return names.map((n2) => ({ m: n2.match(pattern), n: n2 })).filter((x) => x.m !== null).map((x) => ({ id: x.m[1], file: relative(cwd, join18(dir, x.n)), kind }));
|
|
80139
80171
|
};
|
|
80140
80172
|
const all = [
|
|
80141
80173
|
...(await Promise.all(taskDirs.map((dir) => scan(dir, "task")))).flat(),
|
|
@@ -80189,7 +80221,7 @@ function resolveProjectRoot(cwd) {
|
|
|
80189
80221
|
const fs3 = createNodeFileSystem3(cwd);
|
|
80190
80222
|
let current = resolve5(cwd);
|
|
80191
80223
|
while (true) {
|
|
80192
|
-
if (fs3.exists(
|
|
80224
|
+
if (fs3.exists(join18(current, ".spur", "config.yaml"))) {
|
|
80193
80225
|
return current;
|
|
80194
80226
|
}
|
|
80195
80227
|
const parent = dirname13(current);
|
|
@@ -80211,14 +80243,14 @@ function diskReader(cwd) {
|
|
|
80211
80243
|
return {
|
|
80212
80244
|
list: async (dir) => {
|
|
80213
80245
|
try {
|
|
80214
|
-
return (await fs3.readDir(
|
|
80246
|
+
return (await fs3.readDir(join18(cwd, dir))).map((n2) => `${dir}/${n2}`);
|
|
80215
80247
|
} catch {
|
|
80216
80248
|
return [];
|
|
80217
80249
|
}
|
|
80218
80250
|
},
|
|
80219
80251
|
read: async (path9) => {
|
|
80220
80252
|
try {
|
|
80221
|
-
return await fs3.readFile(
|
|
80253
|
+
return await fs3.readFile(join18(cwd, path9));
|
|
80222
80254
|
} catch {
|
|
80223
80255
|
return null;
|
|
80224
80256
|
}
|
|
@@ -80378,7 +80410,7 @@ var init_corpus_check = __esm(() => {
|
|
|
80378
80410
|
});
|
|
80379
80411
|
|
|
80380
80412
|
// ../../packages/app/src/services/corpus-migrator.ts
|
|
80381
|
-
import { join as
|
|
80413
|
+
import { join as join19, relative as relative2 } from "path";
|
|
80382
80414
|
function applyM1(data, flags) {
|
|
80383
80415
|
const raw = data.status;
|
|
80384
80416
|
if (typeof raw !== "string")
|
|
@@ -80678,7 +80710,7 @@ class CorpusMigrator {
|
|
|
80678
80710
|
}
|
|
80679
80711
|
async discoverFiles() {
|
|
80680
80712
|
const entries = await this.fs.readDir(this.corpusDir);
|
|
80681
|
-
return entries.filter((name) => name.endsWith(".md")).filter((name) => name !== "kanban.md").map((name) =>
|
|
80713
|
+
return entries.filter((name) => name.endsWith(".md")).filter((name) => name !== "kanban.md").map((name) => join19(this.corpusDir, name)).sort();
|
|
80682
80714
|
}
|
|
80683
80715
|
async migrateFile(filePath, dryRun, gitDates = new Map) {
|
|
80684
80716
|
const flags = [];
|
|
@@ -86418,8 +86450,13 @@ function schedulerJobTimeoutEnvName(name) {
|
|
|
86418
86450
|
const snake = name.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/([^A-Za-z0-9]+)/g, "_").toUpperCase().replace(/^_+|_+$/g, "");
|
|
86419
86451
|
return `SPUR_SCHEDULER_TIMEOUT_${snake}_MS`;
|
|
86420
86452
|
}
|
|
86421
|
-
function resolveSchedulerJobTimeoutMs(name, env, fallbackMs) {
|
|
86422
|
-
|
|
86453
|
+
function resolveSchedulerJobTimeoutMs(name, env, fallbackMs, declaredMs) {
|
|
86454
|
+
const override = env[schedulerJobTimeoutEnvName(name)];
|
|
86455
|
+
if (override !== undefined)
|
|
86456
|
+
return normalizeLegacyTimeoutMs(override, fallbackMs);
|
|
86457
|
+
if (declaredMs !== undefined)
|
|
86458
|
+
return declaredMs;
|
|
86459
|
+
return fallbackMs;
|
|
86423
86460
|
}
|
|
86424
86461
|
function outputTail(text4) {
|
|
86425
86462
|
const trimmed = text4.trim();
|
|
@@ -86475,6 +86512,10 @@ async function handleSchedulerCustomJob(deps, job) {
|
|
|
86475
86512
|
throw new Error(describeBoundedFailure({ subject: `scheduler job "${payload.name}"`, outcome, outputTail: detail }));
|
|
86476
86513
|
}
|
|
86477
86514
|
if (result.exitCode === null) {
|
|
86515
|
+
if (deps.isShuttingDown?.() === true) {
|
|
86516
|
+
deps.onShutdownAbandon?.(payload.name);
|
|
86517
|
+
return;
|
|
86518
|
+
}
|
|
86478
86519
|
const signalDetail = result.signal === undefined ? "" : ` (${result.signal})`;
|
|
86479
86520
|
throw new Error(`scheduler job "${payload.name}" terminated before a normal exit${signalDetail} ` + `after ${Math.round(outcome.elapsedMs)}ms${detail && `: ${detail}`}`);
|
|
86480
86521
|
}
|
|
@@ -86897,8 +86938,8 @@ import {
|
|
|
86897
86938
|
unlinkSync as unlinkSync2,
|
|
86898
86939
|
writeFileSync as writeFileSync5
|
|
86899
86940
|
} from "fs";
|
|
86900
|
-
import { homedir as
|
|
86901
|
-
import { dirname as dirname14, isAbsolute as isAbsolute3, join as
|
|
86941
|
+
import { homedir as homedir7 } from "os";
|
|
86942
|
+
import { dirname as dirname14, isAbsolute as isAbsolute3, join as join20, resolve as resolve6, sep } from "path";
|
|
86902
86943
|
function parseImporterVersion(version3) {
|
|
86903
86944
|
if (version3 === undefined) {
|
|
86904
86945
|
return null;
|
|
@@ -86953,7 +86994,7 @@ async function buildRefreshCoverage(db2, selector, fanOut) {
|
|
|
86953
86994
|
async function runSessionAugmentedRoots(source, home, cwd, dao2) {
|
|
86954
86995
|
const roots = getSourceDefinition(source).defaultRoots.map((root) => resolve6(home, root));
|
|
86955
86996
|
const runRoots = [];
|
|
86956
|
-
const runRoot =
|
|
86997
|
+
const runRoot = join20(cwd, ".spur", "run");
|
|
86957
86998
|
if (!existsSync6(runRoot))
|
|
86958
86999
|
return { roots, runRoots };
|
|
86959
87000
|
const sourcesByRun = new Map;
|
|
@@ -86963,7 +87004,7 @@ async function runSessionAugmentedRoots(source, home, cwd, dao2) {
|
|
|
86963
87004
|
sourcesByRun.set(row.run_id, sources);
|
|
86964
87005
|
}
|
|
86965
87006
|
for (const runId of readdirSync6(runRoot)) {
|
|
86966
|
-
const sessionsRoot =
|
|
87007
|
+
const sessionsRoot = join20(runRoot, runId, "agent-sessions");
|
|
86967
87008
|
if (!existsSync6(sessionsRoot))
|
|
86968
87009
|
continue;
|
|
86969
87010
|
const mappedSources = sourcesByRun.get(runId);
|
|
@@ -86972,7 +87013,7 @@ async function runSessionAugmentedRoots(source, home, cwd, dao2) {
|
|
|
86972
87013
|
const soleMappedSource = mappedSources?.size === 1 && mappedSources.has(source);
|
|
86973
87014
|
if (!directMatch && !soleMappedSource)
|
|
86974
87015
|
continue;
|
|
86975
|
-
const root = realpathSync4(
|
|
87016
|
+
const root = realpathSync4(join20(sessionsRoot, agent));
|
|
86976
87017
|
roots.push(root);
|
|
86977
87018
|
runRoots.push({ runId, root });
|
|
86978
87019
|
}
|
|
@@ -86993,7 +87034,7 @@ class HistoryService {
|
|
|
86993
87034
|
const startedAt = new Date().toISOString();
|
|
86994
87035
|
const db2 = await this.ctx.getDb();
|
|
86995
87036
|
const dao2 = new RunSessionDao(db2);
|
|
86996
|
-
const discovery = parsedSource !== "opencode" && opts.file === undefined && opts.root === undefined ? await runSessionAugmentedRoots(parsedSource, this.ctx.historyHome ??
|
|
87037
|
+
const discovery = parsedSource !== "opencode" && opts.file === undefined && opts.root === undefined ? await runSessionAugmentedRoots(parsedSource, this.ctx.historyHome ?? homedir7(), this.ctx.cwd ?? process.cwd(), dao2) : undefined;
|
|
86997
87038
|
const result = parsedSource === "opencode" && opts.file === undefined && opts.root === undefined ? await runOpenCodeImport({
|
|
86998
87039
|
db: db2,
|
|
86999
87040
|
sourceDatabase: this.ctx.openCodeSourceDatabase,
|
|
@@ -87060,7 +87101,7 @@ class HistoryService {
|
|
|
87060
87101
|
}
|
|
87061
87102
|
async maintain(opts = {}) {
|
|
87062
87103
|
const db2 = await this.ctx.getDb();
|
|
87063
|
-
const dbPath = this.ctx.cwd ?
|
|
87104
|
+
const dbPath = this.ctx.cwd ? join20(this.ctx.cwd, ".spur", "spur.db") : undefined;
|
|
87064
87105
|
return maintainDatabase(db2, {
|
|
87065
87106
|
...opts,
|
|
87066
87107
|
...dbPath && opts.dbPath === undefined ? { dbPath } : {}
|
|
@@ -87568,9 +87609,9 @@ function resolveArtifactPaths(artifact, opts) {
|
|
|
87568
87609
|
return { artifactPath: out, sidecarPath: `${out.replace(/\.json$/, "")}.errors.jsonl` };
|
|
87569
87610
|
}
|
|
87570
87611
|
const base2 = resolve6(opts.cwd, ".spur", "reports", "history");
|
|
87571
|
-
const dateDir =
|
|
87612
|
+
const dateDir = join20(base2, artifact.generatedAt.slice(0, 10));
|
|
87572
87613
|
const name = `analyze-${selectorDigest(artifact.selector)}`;
|
|
87573
|
-
return { artifactPath:
|
|
87614
|
+
return { artifactPath: join20(dateDir, `${name}.json`), sidecarPath: join20(dateDir, `${name}.errors.jsonl`) };
|
|
87574
87615
|
}
|
|
87575
87616
|
function boundCoverage(coverage) {
|
|
87576
87617
|
const overflow = [];
|
|
@@ -87593,7 +87634,7 @@ function boundCoverage(coverage) {
|
|
|
87593
87634
|
}
|
|
87594
87635
|
function updateLatestPointer(opts, artifactPath) {
|
|
87595
87636
|
const base2 = resolve6(opts.cwd, ".spur", "reports", "history");
|
|
87596
|
-
const latest =
|
|
87637
|
+
const latest = join20(base2, "latest.json");
|
|
87597
87638
|
mkdirSync7(base2, { recursive: true });
|
|
87598
87639
|
if (existsSync6(latest))
|
|
87599
87640
|
unlinkSync2(latest);
|
|
@@ -87659,7 +87700,7 @@ function formatIssue(issue2) {
|
|
|
87659
87700
|
return `${issue2.sourceFile}:${issue2.sourceLine}: ${issue2.reason}`;
|
|
87660
87701
|
}
|
|
87661
87702
|
function pruneReports(cwd, retentionDays = REPORT_RETENTION_DAYS, now2 = new Date) {
|
|
87662
|
-
const dir =
|
|
87703
|
+
const dir = join20(cwd, ".spur", "reports", "history");
|
|
87663
87704
|
let entries;
|
|
87664
87705
|
try {
|
|
87665
87706
|
entries = readdirSync6(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
@@ -87677,7 +87718,7 @@ function pruneReports(cwd, retentionDays = REPORT_RETENTION_DAYS, now2 = new Dat
|
|
|
87677
87718
|
if (entryDate >= cutoff)
|
|
87678
87719
|
continue;
|
|
87679
87720
|
try {
|
|
87680
|
-
rmSync5(
|
|
87721
|
+
rmSync5(join20(dir, name), { recursive: true, force: true });
|
|
87681
87722
|
pruned.push(name);
|
|
87682
87723
|
} catch {}
|
|
87683
87724
|
}
|
|
@@ -87787,7 +87828,7 @@ function extractResolvedWorkflowFacts(workflow) {
|
|
|
87787
87828
|
var init_composition_baseline = () => {};
|
|
87788
87829
|
|
|
87789
87830
|
// ../../packages/app/src/workflow/workflow-resolver.ts
|
|
87790
|
-
import { basename as basename8, isAbsolute as isAbsolute4, join as
|
|
87831
|
+
import { basename as basename8, isAbsolute as isAbsolute4, join as join21, resolve as resolve7 } from "path";
|
|
87791
87832
|
function registeredWorkflowPaths(config4) {
|
|
87792
87833
|
const paths = config4?.workflows?.paths ?? [".spur/workflows/"];
|
|
87793
87834
|
const bundledRoot = bundledConfigRoot();
|
|
@@ -87795,7 +87836,7 @@ function registeredWorkflowPaths(config4) {
|
|
|
87795
87836
|
for (const path9 of paths) {
|
|
87796
87837
|
if (path9.startsWith(BUNDLED_PATH_PREFIX)) {
|
|
87797
87838
|
if (bundledRoot !== null)
|
|
87798
|
-
expanded.push(
|
|
87839
|
+
expanded.push(join21(bundledRoot, path9.slice(BUNDLED_PATH_PREFIX.length)));
|
|
87799
87840
|
} else {
|
|
87800
87841
|
expanded.push(path9);
|
|
87801
87842
|
}
|
|
@@ -87807,7 +87848,7 @@ function normalizeLayerPath(cwd, path9) {
|
|
|
87807
87848
|
}
|
|
87808
87849
|
function workflowLayers(opts) {
|
|
87809
87850
|
const sharedRoot = opts.sharedRoot !== undefined ? opts.sharedRoot : bundledConfigRoot();
|
|
87810
|
-
const sharedPath = sharedRoot !== null ? normalizeLayerPath(opts.cwd,
|
|
87851
|
+
const sharedPath = sharedRoot !== null ? normalizeLayerPath(opts.cwd, join21(sharedRoot, "workflows")) : null;
|
|
87811
87852
|
const layers = [];
|
|
87812
87853
|
const seen = new Set;
|
|
87813
87854
|
const add = (id, path9) => {
|
|
@@ -87816,7 +87857,7 @@ function workflowLayers(opts) {
|
|
|
87816
87857
|
seen.add(path9);
|
|
87817
87858
|
layers.push({ id, path: path9 });
|
|
87818
87859
|
};
|
|
87819
|
-
add("project", normalizeLayerPath(opts.cwd,
|
|
87860
|
+
add("project", normalizeLayerPath(opts.cwd, join21(opts.cwd, ".spur", "workflows")));
|
|
87820
87861
|
for (const entry of opts.registered) {
|
|
87821
87862
|
if (sharedPath !== null && normalizeLayerPath(opts.cwd, entry) === sharedPath)
|
|
87822
87863
|
continue;
|
|
@@ -87860,7 +87901,7 @@ async function scanWorkflowByName(cwd, name, registered) {
|
|
|
87860
87901
|
try {
|
|
87861
87902
|
const files = (await fs3.readDir(layer.path)).filter((f) => f.endsWith(".yaml") || f.endsWith(".yml"));
|
|
87862
87903
|
for (const f of files) {
|
|
87863
|
-
const abs =
|
|
87904
|
+
const abs = join21(layer.path, f);
|
|
87864
87905
|
const wfName = await readWorkflowNameFast(fs3, abs);
|
|
87865
87906
|
if (wfName === name) {
|
|
87866
87907
|
return { path: abs, source: layer.id };
|
|
@@ -87902,16 +87943,16 @@ function resolveWorkflowFile(cwd, file2) {
|
|
|
87902
87943
|
const bundledRoot = bundledConfigRoot();
|
|
87903
87944
|
if (bundledRoot !== null) {
|
|
87904
87945
|
const sharedName = isYaml ? base2 : `${base2}.yaml`;
|
|
87905
|
-
const sharedPath =
|
|
87946
|
+
const sharedPath = join21(bundledRoot, "workflows", sharedName);
|
|
87906
87947
|
if (fs3.exists(sharedPath)) {
|
|
87907
87948
|
return { path: sharedPath, source: "shared" };
|
|
87908
87949
|
}
|
|
87909
87950
|
if (!isYaml) {
|
|
87910
|
-
const sharedPipeline =
|
|
87951
|
+
const sharedPipeline = join21(bundledRoot, "workflows", `${base2}-pipeline.yaml`);
|
|
87911
87952
|
if (fs3.exists(sharedPipeline)) {
|
|
87912
87953
|
return { path: sharedPipeline, source: "shared" };
|
|
87913
87954
|
}
|
|
87914
|
-
const sharedLiteral =
|
|
87955
|
+
const sharedLiteral = join21(bundledRoot, "workflows", base2);
|
|
87915
87956
|
if (fs3.exists(sharedLiteral)) {
|
|
87916
87957
|
return { path: sharedLiteral, source: "shared" };
|
|
87917
87958
|
}
|
|
@@ -88315,7 +88356,7 @@ var init_tripwire = __esm(() => {
|
|
|
88315
88356
|
|
|
88316
88357
|
// ../../packages/app/src/workflow/actions/agent-run.ts
|
|
88317
88358
|
import { tmpdir } from "os";
|
|
88318
|
-
import { dirname as dirname15, isAbsolute as isAbsolute5, join as
|
|
88359
|
+
import { dirname as dirname15, isAbsolute as isAbsolute5, join as join22 } from "path";
|
|
88319
88360
|
|
|
88320
88361
|
class AgentRunActionRunner {
|
|
88321
88362
|
observabilityBus;
|
|
@@ -88344,20 +88385,20 @@ class AgentRunActionRunner {
|
|
|
88344
88385
|
const priority = asOptionalString(options.priority);
|
|
88345
88386
|
const compareExecutorWith = asOptionalString(options.compareExecutorWith) ?? "implement";
|
|
88346
88387
|
const agentLabel = dispatchAgent ?? "<default>";
|
|
88347
|
-
const targetAgentDir = dispatchAgent ?? this.agentConfig.default ?? "
|
|
88388
|
+
const targetAgentDir = dispatchAgent ?? this.agentConfig.default ?? "default";
|
|
88348
88389
|
const prevAgent = freshSession ? undefined : asOptionalString(context4.vars.__agentSessionAgent);
|
|
88349
88390
|
let sessionDir = freshSession ? undefined : asOptionalString(context4.vars.__agentSessionDir);
|
|
88350
88391
|
if (affinityOn) {
|
|
88351
88392
|
if (!sessionDir || prevAgent && prevAgent !== targetAgentDir) {
|
|
88352
|
-
sessionDir =
|
|
88393
|
+
sessionDir = join22(cwd, ".spur", "run", context4.runId, "agent-sessions", targetAgentDir);
|
|
88353
88394
|
}
|
|
88354
88395
|
} else {
|
|
88355
88396
|
if (!sessionDir) {
|
|
88356
|
-
sessionDir =
|
|
88397
|
+
sessionDir = join22(cwd, ".spur", "run", context4.runId, "agent-sessions", targetAgentDir);
|
|
88357
88398
|
}
|
|
88358
88399
|
}
|
|
88359
88400
|
if (freshSession) {
|
|
88360
|
-
sessionDir =
|
|
88401
|
+
sessionDir = join22(cwd, ".spur", "run", context4.runId, "agent-sessions", targetAgentDir, `fresh-${context4.stateOrNodeId}`);
|
|
88361
88402
|
}
|
|
88362
88403
|
const storedSessionId = freshSession ? undefined : asOptionalString(context4.vars.__agentSessionId);
|
|
88363
88404
|
let continueFlag = asOptionalBoolean(options.continue);
|
|
@@ -88449,7 +88490,7 @@ class AgentRunActionRunner {
|
|
|
88449
88490
|
flags.requiresCapabilities = JSON.stringify(requiresCapabilities.requires);
|
|
88450
88491
|
}
|
|
88451
88492
|
if (expectFile !== undefined) {
|
|
88452
|
-
const target = isAbsolute5(expectFile) ? expectFile :
|
|
88493
|
+
const target = isAbsolute5(expectFile) ? expectFile : join22(cwd, expectFile);
|
|
88453
88494
|
const fs3 = createNodeFileSystem3(cwd);
|
|
88454
88495
|
if (await fs3.exists(target)) {
|
|
88455
88496
|
try {
|
|
@@ -88468,7 +88509,7 @@ class AgentRunActionRunner {
|
|
|
88468
88509
|
return;
|
|
88469
88510
|
return `
|
|
88470
88511
|
|
|
88471
|
-
Write the required artifact to this absolute path: ${
|
|
88512
|
+
Write the required artifact to this absolute path: ${join22(cwd, rel)}`;
|
|
88472
88513
|
})();
|
|
88473
88514
|
const dispatchInput = input === undefined || absoluteArtifactHint === undefined ? input : `${input}${absoluteArtifactHint}`;
|
|
88474
88515
|
const observer = this.observabilityBus === undefined ? undefined : (event) => {
|
|
@@ -88622,13 +88663,13 @@ Write the required artifact to this absolute path: ${join21(cwd, rel)}`;
|
|
|
88622
88663
|
}
|
|
88623
88664
|
}
|
|
88624
88665
|
if (capture && answerFile !== undefined) {
|
|
88625
|
-
const target = isAbsolute5(answerFile) ? answerFile :
|
|
88666
|
+
const target = isAbsolute5(answerFile) ? answerFile : join22(cwd, answerFile);
|
|
88626
88667
|
const fs3 = createNodeFileSystem3(cwd);
|
|
88627
88668
|
await fs3.ensureDir(dirname15(target));
|
|
88628
88669
|
await fs3.writeFile(target, answer);
|
|
88629
88670
|
}
|
|
88630
88671
|
if (ok && expectFile !== undefined) {
|
|
88631
|
-
const target = isAbsolute5(expectFile) ? expectFile :
|
|
88672
|
+
const target = isAbsolute5(expectFile) ? expectFile : join22(cwd, expectFile);
|
|
88632
88673
|
const fs3 = createNodeFileSystem3(cwd);
|
|
88633
88674
|
if (!await fs3.exists(target)) {
|
|
88634
88675
|
await writePartialWorkArtifact(context4, agentLabel, model, traced, cwd, sessionDir, this.agentConfig.secretValues);
|
|
@@ -88673,7 +88714,7 @@ ${traced.stdout ?? ""}`);
|
|
|
88673
88714
|
const resolvedAgent = invocation?.agent ?? targetAgentDir;
|
|
88674
88715
|
let resolvedSessionDir = sessionDir;
|
|
88675
88716
|
if (ok && affinityOn && resolvedAgent !== targetAgentDir && !context4.vars.__agentSessionDir) {
|
|
88676
|
-
resolvedSessionDir =
|
|
88717
|
+
resolvedSessionDir = join22(cwd, ".spur", "run", context4.runId, "agent-sessions", resolvedAgent);
|
|
88677
88718
|
}
|
|
88678
88719
|
let discoveredSessionId = storedSessionId;
|
|
88679
88720
|
if (ok && affinityOn && resolvedSessionDir && !discoveredSessionId) {
|
|
@@ -88681,7 +88722,7 @@ ${traced.stdout ?? ""}`);
|
|
|
88681
88722
|
}
|
|
88682
88723
|
if (ok && affinityOn && resolvedSessionDir) {
|
|
88683
88724
|
try {
|
|
88684
|
-
const sidecarPath =
|
|
88725
|
+
const sidecarPath = join22(cwd, ".spur", "run", `${context4.runId}-agent-session.json`);
|
|
88685
88726
|
const fs3 = createNodeFileSystem3(cwd);
|
|
88686
88727
|
await fs3.ensureDir(dirname15(sidecarPath));
|
|
88687
88728
|
await fs3.writeFile(sidecarPath, JSON.stringify({
|
|
@@ -88730,7 +88771,7 @@ async function discoverSessionId(sessionDir) {
|
|
|
88730
88771
|
let newestFile;
|
|
88731
88772
|
let newestMtime = 0;
|
|
88732
88773
|
for (const entry of jsonEntries) {
|
|
88733
|
-
const fullPath =
|
|
88774
|
+
const fullPath = join22(sessionDir, entry);
|
|
88734
88775
|
const st = await fs3.stat(fullPath);
|
|
88735
88776
|
if (st?.isFile() && st.mtimeMs > newestMtime) {
|
|
88736
88777
|
newestMtime = st.mtimeMs;
|
|
@@ -88826,7 +88867,7 @@ async function writePartialWorkArtifact(context4, agentLabel, model, traced, cwd
|
|
|
88826
88867
|
const headerLine = model !== undefined ? `${agentLabel} (model: ${model})` : agentLabel;
|
|
88827
88868
|
const inv = traced.invocation;
|
|
88828
88869
|
const argvLine = redactAndBound(inv ? `${inv.command} ${inv.argv.join(" ")}` : "(invocation not captured)", secretValues, PARTIAL_ARTIFACT_TAIL_CHARS);
|
|
88829
|
-
const latchedSessionPath =
|
|
88870
|
+
const latchedSessionPath = join22(cwd, ".spur", "run", `${context4.runId}-agent-session.json`);
|
|
88830
88871
|
const resumeContext = [
|
|
88831
88872
|
"",
|
|
88832
88873
|
"## resume context",
|
|
@@ -88842,9 +88883,9 @@ async function writePartialWorkArtifact(context4, agentLabel, model, traced, cwd
|
|
|
88842
88883
|
try {
|
|
88843
88884
|
const fs4 = createNodeFileSystem3(cwd);
|
|
88844
88885
|
const locator = TaskLocator.forDirs(fs4, [
|
|
88845
|
-
|
|
88846
|
-
|
|
88847
|
-
|
|
88886
|
+
join22(cwd, "docs", "tasks3"),
|
|
88887
|
+
join22(cwd, "docs", "tasks2"),
|
|
88888
|
+
join22(cwd, "docs", "tasks")
|
|
88848
88889
|
]);
|
|
88849
88890
|
const hit = await locator.findByWbs(wbs);
|
|
88850
88891
|
if (hit) {
|
|
@@ -88900,7 +88941,7 @@ async function writePartialWorkArtifact(context4, agentLabel, model, traced, cwd
|
|
|
88900
88941
|
completedSection
|
|
88901
88942
|
].join(`
|
|
88902
88943
|
`);
|
|
88903
|
-
const target =
|
|
88944
|
+
const target = join22(cwd, ".spur", "run", `${context4.runId}-${context4.stateOrNodeId}-partial.md`);
|
|
88904
88945
|
const fs3 = createNodeFileSystem3(cwd);
|
|
88905
88946
|
await fs3.ensureDir(dirname15(target));
|
|
88906
88947
|
await fs3.writeFile(target, body);
|
|
@@ -88922,7 +88963,7 @@ async function gitDiffStat(cwd) {
|
|
|
88922
88963
|
}
|
|
88923
88964
|
}
|
|
88924
88965
|
async function createGitWorkingTreeSnapshot(cwd, excludeGlobs = ["docs/tasks3/*", "docs/features/*"]) {
|
|
88925
|
-
const indexFile =
|
|
88966
|
+
const indexFile = join22(tmpdir(), `spur-implement-scope-${crypto.randomUUID()}.index`);
|
|
88926
88967
|
const fs3 = createNodeFileSystem3(cwd);
|
|
88927
88968
|
let keepIndex = false;
|
|
88928
88969
|
try {
|
|
@@ -89053,9 +89094,9 @@ async function findOutOfScopeChanges(cwd, wbs, changed) {
|
|
|
89053
89094
|
try {
|
|
89054
89095
|
const fs3 = createNodeFileSystem3(cwd);
|
|
89055
89096
|
const locator = TaskLocator.forDirs(fs3, [
|
|
89056
|
-
|
|
89057
|
-
|
|
89058
|
-
|
|
89097
|
+
join22(cwd, "docs", "tasks3"),
|
|
89098
|
+
join22(cwd, "docs", "tasks2"),
|
|
89099
|
+
join22(cwd, "docs", "tasks")
|
|
89059
89100
|
]);
|
|
89060
89101
|
const hit = await locator.findByWbs(wbs);
|
|
89061
89102
|
if (!hit)
|
|
@@ -89105,7 +89146,7 @@ var init_child_env = __esm(() => {
|
|
|
89105
89146
|
});
|
|
89106
89147
|
|
|
89107
89148
|
// ../../packages/app/src/workflow/actions/run-path.ts
|
|
89108
|
-
import { dirname as dirname16, join as
|
|
89149
|
+
import { dirname as dirname16, join as join23, normalize, relative as relative3, resolve as resolve8, sep as sep2 } from "path";
|
|
89109
89150
|
function within(child, ancestor) {
|
|
89110
89151
|
return child === ancestor || child.startsWith(`${ancestor}${sep2}`);
|
|
89111
89152
|
}
|
|
@@ -89114,7 +89155,7 @@ async function resolveRunArtifactPath(fileSystem, workdir, pathRaw) {
|
|
|
89114
89155
|
throw new RunArtifactPathError("physical path confinement requires a FileSystem with realPath support \u2014 refusing to skip confinement (0785 R2)");
|
|
89115
89156
|
}
|
|
89116
89157
|
const lexicalTarget = normalize(resolve8(workdir, pathRaw));
|
|
89117
|
-
const lexicalRunRoot = normalize(
|
|
89158
|
+
const lexicalRunRoot = normalize(join23(resolve8(workdir), ".spur", "run"));
|
|
89118
89159
|
if (!lexicalTarget.startsWith(`${lexicalRunRoot}${sep2}`)) {
|
|
89119
89160
|
throw new RunArtifactPathError(`must resolve beneath .spur/run/ (got ${pathRaw})`);
|
|
89120
89161
|
}
|
|
@@ -89125,7 +89166,7 @@ async function resolveRunArtifactPath(fileSystem, workdir, pathRaw) {
|
|
|
89125
89166
|
} catch (error51) {
|
|
89126
89167
|
throw new RunArtifactPathError(`project workdir could not be canonicalized: ${error51.message}`);
|
|
89127
89168
|
}
|
|
89128
|
-
const canonicalRunRootCandidate =
|
|
89169
|
+
const canonicalRunRootCandidate = join23(canonicalWorkdir, ".spur", "run");
|
|
89129
89170
|
let canonicalRunRoot = canonicalRunRootCandidate;
|
|
89130
89171
|
const rootStat = await fileSystem.stat(canonicalRunRootCandidate);
|
|
89131
89172
|
if (rootStat !== null) {
|
|
@@ -89173,7 +89214,7 @@ async function resolveRunArtifactPath(fileSystem, workdir, pathRaw) {
|
|
|
89173
89214
|
if (!within(anchor, canonicalWorkdir)) {
|
|
89174
89215
|
throw new RunArtifactPathError(`ancestor ${probe} escapes the project workdir through a symlink`);
|
|
89175
89216
|
}
|
|
89176
|
-
const reconstructed = normalize(
|
|
89217
|
+
const reconstructed = normalize(join23(anchor, relative3(probe, lexicalTarget)));
|
|
89177
89218
|
if (!reconstructed.startsWith(`${canonicalRunRoot}${sep2}`)) {
|
|
89178
89219
|
throw new RunArtifactPathError(`path escapes .spur/run/ through a symlink (got ${pathRaw})`);
|
|
89179
89220
|
}
|
|
@@ -89190,7 +89231,7 @@ var init_run_path = __esm(() => {
|
|
|
89190
89231
|
});
|
|
89191
89232
|
|
|
89192
89233
|
// ../../packages/app/src/workflow/actions/command-gate.ts
|
|
89193
|
-
import { join as
|
|
89234
|
+
import { join as join24, normalize as normalize2 } from "path";
|
|
89194
89235
|
function sleep2(ms) {
|
|
89195
89236
|
return new Promise((resolve9) => setTimeout(resolve9, ms));
|
|
89196
89237
|
}
|
|
@@ -89262,7 +89303,7 @@ class CommandGateActionRunner {
|
|
|
89262
89303
|
const retryOn = Array.isArray(retry.on) ? retry.on : [];
|
|
89263
89304
|
const timeoutMs = typeof options.timeoutMs === "number" ? options.timeoutMs : undefined;
|
|
89264
89305
|
const env = childProcessEnv(context4.vars);
|
|
89265
|
-
await this.fileSystem.ensureDir(normalize2(
|
|
89306
|
+
await this.fileSystem.ensureDir(normalize2(join24(normalized, "..")));
|
|
89266
89307
|
let lastStdout = "";
|
|
89267
89308
|
let lastStderr = "";
|
|
89268
89309
|
let lastExitCode = -1;
|
|
@@ -89919,7 +89960,7 @@ var init_http_request = __esm(() => {
|
|
|
89919
89960
|
// ../../packages/app/src/workflow/proof-input-fingerprint.ts
|
|
89920
89961
|
import crypto4 from "crypto";
|
|
89921
89962
|
import { tmpdir as tmpdir2 } from "os";
|
|
89922
|
-
import { dirname as dirname17, join as
|
|
89963
|
+
import { dirname as dirname17, join as join25, resolve as resolve10, sep as sep4 } from "path";
|
|
89923
89964
|
function taskDocumentShapeError(content) {
|
|
89924
89965
|
const doc2 = MarkdownDocument.parse(content, "task");
|
|
89925
89966
|
const heading = CANONICAL_TASK_HEADING_RE.exec(doc2.bodyWithoutFrontmatter);
|
|
@@ -89994,7 +90035,7 @@ async function readProofInputContents(fileSystem = createNodeFileSystem3(), work
|
|
|
89994
90035
|
};
|
|
89995
90036
|
}
|
|
89996
90037
|
async function createGitAlternateTree(cwd, excludeGlobs = DEFAULT_EXCLUDE_GLOBS, executor = new NodeProcessExecutor3, fs3 = createNodeFileSystem3()) {
|
|
89997
|
-
const indexFile =
|
|
90038
|
+
const indexFile = join25(tmpdir2(), `spur-proof-fingerprint-${crypto4.randomUUID()}.index`);
|
|
89998
90039
|
try {
|
|
89999
90040
|
await fs3.ensureDir(dirname17(indexFile));
|
|
90000
90041
|
const env = Object.fromEntries(Object.entries(process.env).filter((entry) => entry[1] !== undefined));
|
|
@@ -90289,7 +90330,7 @@ var init_rule_check = __esm(() => {
|
|
|
90289
90330
|
});
|
|
90290
90331
|
|
|
90291
90332
|
// ../../packages/app/src/workflow/actions/run-artifact.ts
|
|
90292
|
-
import { join as
|
|
90333
|
+
import { join as join26, resolve as resolve11 } from "path";
|
|
90293
90334
|
|
|
90294
90335
|
class RunArtifactActionRunner {
|
|
90295
90336
|
getDb;
|
|
@@ -90567,7 +90608,7 @@ class RunArtifactActionRunner {
|
|
|
90567
90608
|
error: `${KIND14}: proof.stages.review.status is "${String(review.status)}" \u2014 a skipped or stale review is not completed evidence (0785 R4)`
|
|
90568
90609
|
};
|
|
90569
90610
|
}
|
|
90570
|
-
const markerPath =
|
|
90611
|
+
const markerPath = join26(resolve11(workdir), ".spur", "run", `${context4.runId}-review-proof.digest`);
|
|
90571
90612
|
let marker = null;
|
|
90572
90613
|
try {
|
|
90573
90614
|
marker = await this.fileSystem.readFile(markerPath);
|
|
@@ -90911,7 +90952,7 @@ var init_checkpoint_contract = __esm(() => {
|
|
|
90911
90952
|
|
|
90912
90953
|
// ../../packages/app/src/services/workflow-service.ts
|
|
90913
90954
|
import { realpathSync as realpathSync5 } from "fs";
|
|
90914
|
-
import { basename as basename9, dirname as dirname18, isAbsolute as isAbsolute6, join as
|
|
90955
|
+
import { basename as basename9, dirname as dirname18, isAbsolute as isAbsolute6, join as join27, resolve as resolve12, sep as sep5 } from "path";
|
|
90915
90956
|
function signalSubprocess(pid) {
|
|
90916
90957
|
if (!Number.isInteger(pid) || pid <= 1)
|
|
90917
90958
|
return false;
|
|
@@ -91176,7 +91217,7 @@ class WorkflowAppService {
|
|
|
91176
91217
|
};
|
|
91177
91218
|
}
|
|
91178
91219
|
async cleanRunLogs(retentionDays = 30, dryRun = false) {
|
|
91179
|
-
const runDir =
|
|
91220
|
+
const runDir = join27(this.ctx.cwd, ".spur", "run");
|
|
91180
91221
|
const fs3 = createNodeFileSystem3();
|
|
91181
91222
|
const cutoffMs = Date.now() - retentionDays * 24 * 60 * 60 * 1000;
|
|
91182
91223
|
const reclaimed = [];
|
|
@@ -91188,7 +91229,7 @@ class WorkflowAppService {
|
|
|
91188
91229
|
return { retentionDays, dryRun, reclaimed, failures };
|
|
91189
91230
|
}
|
|
91190
91231
|
for (const name of entries) {
|
|
91191
|
-
const path9 =
|
|
91232
|
+
const path9 = join27(runDir, name);
|
|
91192
91233
|
const stat2 = await fs3.stat(path9);
|
|
91193
91234
|
if (stat2 === null || !stat2.isFile() || stat2.mtimeMs >= cutoffMs)
|
|
91194
91235
|
continue;
|
|
@@ -91207,7 +91248,7 @@ class WorkflowAppService {
|
|
|
91207
91248
|
return { retentionDays, dryRun, reclaimed, failures };
|
|
91208
91249
|
}
|
|
91209
91250
|
async cleanCheckpoints(retentionDays = 30, dryRun = false) {
|
|
91210
|
-
const sessionsDir =
|
|
91251
|
+
const sessionsDir = join27(this.ctx.cwd, ".spur", "memory", "sessions");
|
|
91211
91252
|
const fs3 = createNodeFileSystem3();
|
|
91212
91253
|
let sessionsReal;
|
|
91213
91254
|
try {
|
|
@@ -91227,7 +91268,7 @@ class WorkflowAppService {
|
|
|
91227
91268
|
}
|
|
91228
91269
|
const activeRunIds = new Set((await new RunDao(await this.ctx.getDb()).listActiveRuns()).map((r) => r.id));
|
|
91229
91270
|
for (const name of entries) {
|
|
91230
|
-
const path9 =
|
|
91271
|
+
const path9 = join27(sessionsDir, name);
|
|
91231
91272
|
let resolved;
|
|
91232
91273
|
try {
|
|
91233
91274
|
resolved = realpathSync5(path9);
|
|
@@ -91427,7 +91468,7 @@ class WorkflowAppService {
|
|
|
91427
91468
|
await new RunDao(db2).stampFailureReason(runId, result.reason);
|
|
91428
91469
|
}
|
|
91429
91470
|
async validateResumeCheckpointFreshness(runId, row, workdir) {
|
|
91430
|
-
const sessionsDir =
|
|
91471
|
+
const sessionsDir = join27(workdir, ".spur", "memory", "sessions");
|
|
91431
91472
|
const fs3 = createNodeFileSystem3();
|
|
91432
91473
|
let entries;
|
|
91433
91474
|
try {
|
|
@@ -91461,7 +91502,7 @@ class WorkflowAppService {
|
|
|
91461
91502
|
return head;
|
|
91462
91503
|
};
|
|
91463
91504
|
for (const name of entries) {
|
|
91464
|
-
const filePath =
|
|
91505
|
+
const filePath = join27(sessionsDir, name);
|
|
91465
91506
|
let raw;
|
|
91466
91507
|
try {
|
|
91467
91508
|
raw = await fs3.readFile(filePath);
|
|
@@ -91492,7 +91533,7 @@ class WorkflowAppService {
|
|
|
91492
91533
|
const staleness = checkpointStaleness(meta3, {
|
|
91493
91534
|
...wbs !== undefined ? { taskWbs: wbs } : {},
|
|
91494
91535
|
...meta3.sourceCommit === "" ? {} : { sourceCommit: await resolveHead() },
|
|
91495
|
-
artifactExists: (p) => fs3.exists(isAbsolute6(p) ? p :
|
|
91536
|
+
artifactExists: (p) => fs3.exists(isAbsolute6(p) ? p : join27(workdir, p))
|
|
91496
91537
|
});
|
|
91497
91538
|
if (staleness.stale) {
|
|
91498
91539
|
throw new Error(`Cannot resume run "${runId}": checkpoint is stale (${staleness.reason}).`);
|
|
@@ -92037,8 +92078,8 @@ function resolveOutputLogConfig(config4) {
|
|
|
92037
92078
|
};
|
|
92038
92079
|
}
|
|
92039
92080
|
async function outputArtifactForRun(cwd, runId) {
|
|
92040
|
-
const relative4 =
|
|
92041
|
-
return await fileExists(
|
|
92081
|
+
const relative4 = join27(".spur", "run", `${runId}.log`);
|
|
92082
|
+
return await fileExists(join27(cwd, relative4)) ? relative4 : undefined;
|
|
92042
92083
|
}
|
|
92043
92084
|
async function scanWorkflowFiles(rootPath, source, loadEntry) {
|
|
92044
92085
|
const entries = [];
|
|
@@ -92236,7 +92277,7 @@ function projectActionTraceResult(resultJson, secretValues = []) {
|
|
|
92236
92277
|
async function partialArtifactForAction(cwd, runId, node, ok) {
|
|
92237
92278
|
if (ok !== false || !TRACE_IDENTIFIER.test(runId) || !TRACE_IDENTIFIER.test(node))
|
|
92238
92279
|
return;
|
|
92239
|
-
const relativePath2 =
|
|
92280
|
+
const relativePath2 = join27(".spur", "run", `${runId}-${node}-partial.md`);
|
|
92240
92281
|
return await fileExists(resolve12(cwd, relativePath2)) ? relativePath2 : undefined;
|
|
92241
92282
|
}
|
|
92242
92283
|
var TASK_PIPELINE_WORKFLOW = "task-pipeline", PIPELINE_LINK_KIND = "pipeline", COMPOSITION_CAPS, STRUCTURE_TOKENS, TRACE_IDENTIFIER, TRACE_RESULT_FIELDS, TRACE_INVOCATION_FIELDS;
|
|
@@ -92281,7 +92322,7 @@ var init_workflow_service = __esm(() => {
|
|
|
92281
92322
|
});
|
|
92282
92323
|
|
|
92283
92324
|
// ../../packages/app/src/services/inline-run-setup.ts
|
|
92284
|
-
import { join as
|
|
92325
|
+
import { join as join28, resolve as resolve13 } from "path";
|
|
92285
92326
|
function parseIdentityMetadata(raw) {
|
|
92286
92327
|
if (raw.trim() === "")
|
|
92287
92328
|
return {};
|
|
@@ -92295,8 +92336,8 @@ function parseIdentityMetadata(raw) {
|
|
|
92295
92336
|
}
|
|
92296
92337
|
}
|
|
92297
92338
|
async function openInlineRunProjectDb(workdir) {
|
|
92298
|
-
const url2 =
|
|
92299
|
-
mkdirSync8(
|
|
92339
|
+
const url2 = join28(resolve13(workdir), ".spur", "spur.db");
|
|
92340
|
+
mkdirSync8(join28(url2, ".."), { recursive: true });
|
|
92300
92341
|
const adapter = await createMigratedDb({ url: url2 });
|
|
92301
92342
|
return { adapter, close: () => adapter.close() };
|
|
92302
92343
|
}
|
|
@@ -93248,8 +93289,8 @@ var init_project_start = __esm(() => {
|
|
|
93248
93289
|
});
|
|
93249
93290
|
|
|
93250
93291
|
// ../../packages/app/src/services/rule-service.ts
|
|
93251
|
-
import { homedir as
|
|
93252
|
-
import { delimiter, join as
|
|
93292
|
+
import { homedir as homedir8 } from "os";
|
|
93293
|
+
import { delimiter, join as join29, relative as relative4, resolve as resolve15 } from "path";
|
|
93253
93294
|
|
|
93254
93295
|
class RuleService {
|
|
93255
93296
|
context;
|
|
@@ -93444,13 +93485,13 @@ ${ruleIds.join(`
|
|
|
93444
93485
|
const hasGlobalOverride = globalOverride !== undefined && globalOverride.length > 0;
|
|
93445
93486
|
layers.push({
|
|
93446
93487
|
id: "global",
|
|
93447
|
-
path: hasGlobalOverride ? resolve15(cwd, globalOverride) :
|
|
93488
|
+
path: hasGlobalOverride ? resolve15(cwd, globalOverride) : join29(homedir8(), GLOBAL_RULES_DIR),
|
|
93448
93489
|
priority: 10
|
|
93449
93490
|
});
|
|
93450
93491
|
if (opts.includeBundled && !hasGlobalOverride) {
|
|
93451
93492
|
const bundledConfig = bundledConfigRoot();
|
|
93452
93493
|
if (bundledConfig !== null) {
|
|
93453
|
-
layers.push({ id: "bundled-config", path:
|
|
93494
|
+
layers.push({ id: "bundled-config", path: join29(bundledConfig, "rules"), priority: 15 });
|
|
93454
93495
|
}
|
|
93455
93496
|
const bundled = await bundledRulesRoot();
|
|
93456
93497
|
if (bundled !== null)
|
|
@@ -93663,7 +93704,7 @@ ${ruleIds.join(`
|
|
|
93663
93704
|
const { fs: fs3 } = this.context;
|
|
93664
93705
|
for (const root of await this.ruleRoots()) {
|
|
93665
93706
|
for (const ext of ["yaml", "yml", "json"]) {
|
|
93666
|
-
if (await fs3.exists(
|
|
93707
|
+
if (await fs3.exists(join29(root, `${name}.${ext}`)))
|
|
93667
93708
|
return true;
|
|
93668
93709
|
}
|
|
93669
93710
|
}
|
|
@@ -93699,7 +93740,7 @@ ${ruleIds.join(`
|
|
|
93699
93740
|
for (const layer of await this.existingRuleSourceLayers()) {
|
|
93700
93741
|
const entries = await this.context.fs.readDir(layer.path);
|
|
93701
93742
|
const named = entries.map((entry) => ({ entry, name: entry.match(/^([\w-]+)\.(?:ya?ml|json)$/i)?.[1] ?? "" })).filter(({ name }) => name.length > 0);
|
|
93702
|
-
const stats = await Promise.all(named.map(({ entry }) => this.context.fs.stat(
|
|
93743
|
+
const stats = await Promise.all(named.map(({ entry }) => this.context.fs.stat(join29(layer.path, entry))));
|
|
93703
93744
|
named.forEach(({ name }, i2) => {
|
|
93704
93745
|
if (stats[i2]?.isFile())
|
|
93705
93746
|
candidates.add(name);
|
|
@@ -93721,7 +93762,7 @@ ${ruleIds.join(`
|
|
|
93721
93762
|
for (const layer of layers) {
|
|
93722
93763
|
for (const file2 of await this.listRuleFilesInLayer(layer.path)) {
|
|
93723
93764
|
if (!merged.has(file2))
|
|
93724
|
-
merged.set(file2, { absolutePath:
|
|
93765
|
+
merged.set(file2, { absolutePath: join29(layer.path, file2), source: layer.id });
|
|
93725
93766
|
}
|
|
93726
93767
|
}
|
|
93727
93768
|
const files = [];
|
|
@@ -93789,7 +93830,7 @@ ${ruleIds.join(`
|
|
|
93789
93830
|
for (const entry of entries.sort()) {
|
|
93790
93831
|
if (entry === "presets")
|
|
93791
93832
|
continue;
|
|
93792
|
-
const stat2 = await this.context.fs.stat(
|
|
93833
|
+
const stat2 = await this.context.fs.stat(join29(root, entry));
|
|
93793
93834
|
if (stat2?.isDirectory())
|
|
93794
93835
|
dirs.push(entry);
|
|
93795
93836
|
}
|
|
@@ -93797,12 +93838,12 @@ ${ruleIds.join(`
|
|
|
93797
93838
|
}
|
|
93798
93839
|
async listRuleFiles(root, relativeDir) {
|
|
93799
93840
|
const { fs: fs3 } = this.context;
|
|
93800
|
-
const dir =
|
|
93841
|
+
const dir = join29(root, relativeDir);
|
|
93801
93842
|
const entries = await fs3.readDir(dir);
|
|
93802
93843
|
const files = [];
|
|
93803
93844
|
for (const entry of entries.sort()) {
|
|
93804
|
-
const relativePath2 = relativeDir.length === 0 ? entry :
|
|
93805
|
-
const absolutePath =
|
|
93845
|
+
const relativePath2 = relativeDir.length === 0 ? entry : join29(relativeDir, entry);
|
|
93846
|
+
const absolutePath = join29(root, relativePath2);
|
|
93806
93847
|
const stat2 = await fs3.stat(absolutePath);
|
|
93807
93848
|
if (stat2?.isDirectory()) {
|
|
93808
93849
|
files.push(...await this.listRuleFiles(root, relativePath2));
|
|
@@ -93936,8 +93977,8 @@ var init_rule_service = __esm(() => {
|
|
|
93936
93977
|
init_dist10();
|
|
93937
93978
|
init_agent_execution();
|
|
93938
93979
|
init_system_event_envelope();
|
|
93939
|
-
LOCAL_RULES_DIR =
|
|
93940
|
-
GLOBAL_RULES_DIR =
|
|
93980
|
+
LOCAL_RULES_DIR = join29(".spur", "rules");
|
|
93981
|
+
GLOBAL_RULES_DIR = join29(".config", "spur", "rules");
|
|
93941
93982
|
SEVERITY_RANK2 = {
|
|
93942
93983
|
info: 0,
|
|
93943
93984
|
warning: 1,
|
|
@@ -94152,6 +94193,572 @@ var init_run_store_service = __esm(() => {
|
|
|
94152
94193
|
};
|
|
94153
94194
|
});
|
|
94154
94195
|
|
|
94196
|
+
// ../../packages/app/src/services/slash-commands-service.ts
|
|
94197
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync9, readdirSync as readdirSync7, readFileSync as readFileSync10, writeFileSync as writeFileSync6 } from "fs";
|
|
94198
|
+
import { homedir as homedir9 } from "os";
|
|
94199
|
+
import { basename as basename11, dirname as dirname19, join as join30, resolve as resolve16 } from "path";
|
|
94200
|
+
function getBuiltinClaudeCommands() {
|
|
94201
|
+
return BUILTIN_CLAUDE_COMMANDS.map((cmd) => ({ ...cmd }));
|
|
94202
|
+
}
|
|
94203
|
+
function deduceCommandCategory(nameOrStem) {
|
|
94204
|
+
const lower = nameOrStem.toLowerCase();
|
|
94205
|
+
if (/(git|commit|review|revert|pr-review|diff|branch|stash)/.test(lower)) {
|
|
94206
|
+
return "git";
|
|
94207
|
+
}
|
|
94208
|
+
if (/(rule|workflow|init|harness|spur|plan|run|verify|wrap|refine|parallel|idea)/.test(lower)) {
|
|
94209
|
+
return "harness";
|
|
94210
|
+
}
|
|
94211
|
+
if (/(doctor|cost|compact|clear|terminal|\bbug\b|config|login|logout|sys|status|ps)/.test(lower)) {
|
|
94212
|
+
return "sys";
|
|
94213
|
+
}
|
|
94214
|
+
return "dev";
|
|
94215
|
+
}
|
|
94216
|
+
function findPluginsSpCommandsDir(startDir = process.cwd()) {
|
|
94217
|
+
let current = resolve16(startDir);
|
|
94218
|
+
while (true) {
|
|
94219
|
+
try {
|
|
94220
|
+
const candidate = join30(current, "plugins", "sp", "commands");
|
|
94221
|
+
if (existsSync8(candidate)) {
|
|
94222
|
+
return candidate;
|
|
94223
|
+
}
|
|
94224
|
+
} catch {
|
|
94225
|
+
break;
|
|
94226
|
+
}
|
|
94227
|
+
const parent = dirname19(current);
|
|
94228
|
+
if (parent === current) {
|
|
94229
|
+
break;
|
|
94230
|
+
}
|
|
94231
|
+
current = parent;
|
|
94232
|
+
}
|
|
94233
|
+
return null;
|
|
94234
|
+
}
|
|
94235
|
+
function findProjectClaudeCommandsDir(startDir = process.cwd()) {
|
|
94236
|
+
let current = resolve16(startDir);
|
|
94237
|
+
while (true) {
|
|
94238
|
+
try {
|
|
94239
|
+
const candidate = join30(current, ".claude", "commands");
|
|
94240
|
+
if (existsSync8(candidate)) {
|
|
94241
|
+
return candidate;
|
|
94242
|
+
}
|
|
94243
|
+
} catch {
|
|
94244
|
+
break;
|
|
94245
|
+
}
|
|
94246
|
+
const parent = dirname19(current);
|
|
94247
|
+
if (parent === current) {
|
|
94248
|
+
break;
|
|
94249
|
+
}
|
|
94250
|
+
current = parent;
|
|
94251
|
+
}
|
|
94252
|
+
return null;
|
|
94253
|
+
}
|
|
94254
|
+
function parseMarkdownFrontmatter(rawContent) {
|
|
94255
|
+
const trimmed = rawContent.trimStart();
|
|
94256
|
+
if (!trimmed.startsWith("---")) {
|
|
94257
|
+
return { frontmatter: {}, body: rawContent };
|
|
94258
|
+
}
|
|
94259
|
+
const match = trimmed.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);
|
|
94260
|
+
if (!match) {
|
|
94261
|
+
return { frontmatter: {}, body: rawContent };
|
|
94262
|
+
}
|
|
94263
|
+
try {
|
|
94264
|
+
const parsed = $parse(match[1] ?? "");
|
|
94265
|
+
return {
|
|
94266
|
+
frontmatter: typeof parsed === "object" && parsed !== null ? parsed : {},
|
|
94267
|
+
body: match[2] ?? ""
|
|
94268
|
+
};
|
|
94269
|
+
} catch {
|
|
94270
|
+
return { frontmatter: {}, body: rawContent };
|
|
94271
|
+
}
|
|
94272
|
+
}
|
|
94273
|
+
function scanMarkdownCommands(dirPath, options = {}) {
|
|
94274
|
+
if (!existsSync8(dirPath)) {
|
|
94275
|
+
return [];
|
|
94276
|
+
}
|
|
94277
|
+
const { prefix = "/", source = "plugin:sp", defaultCategory, pluginId } = options;
|
|
94278
|
+
const items = [];
|
|
94279
|
+
try {
|
|
94280
|
+
const entries = readdirSync7(dirPath, { withFileTypes: true });
|
|
94281
|
+
for (const entry of entries) {
|
|
94282
|
+
if (!entry.isFile() || !entry.name.endsWith(".md")) {
|
|
94283
|
+
continue;
|
|
94284
|
+
}
|
|
94285
|
+
const fullPath = join30(dirPath, entry.name);
|
|
94286
|
+
const stem = basename11(entry.name, ".md");
|
|
94287
|
+
const fileContent = readFileSync10(fullPath, "utf-8");
|
|
94288
|
+
const { frontmatter, body } = parseMarkdownFrontmatter(fileContent);
|
|
94289
|
+
const cleanPrefix = prefix.endsWith(":") ? prefix : prefix.endsWith("/") ? prefix : `${prefix}:`;
|
|
94290
|
+
const name = prefix === "/" ? `/${stem}` : `${cleanPrefix}${stem}`;
|
|
94291
|
+
let description = "";
|
|
94292
|
+
if (typeof frontmatter.description === "string" && frontmatter.description.trim().length > 0) {
|
|
94293
|
+
description = frontmatter.description.trim();
|
|
94294
|
+
} else {
|
|
94295
|
+
const lines = body.split(`
|
|
94296
|
+
`).map((l) => l.trim()).filter((l) => l.length > 0);
|
|
94297
|
+
const firstHeading = lines.find((l) => l.startsWith("# "));
|
|
94298
|
+
const firstLine = lines[0];
|
|
94299
|
+
if (firstHeading) {
|
|
94300
|
+
description = firstHeading.replace(/^#\s+/, "").trim();
|
|
94301
|
+
} else if (firstLine !== undefined) {
|
|
94302
|
+
description = firstLine.replace(/^[#*-\s]+/, "").trim();
|
|
94303
|
+
} else {
|
|
94304
|
+
description = stem.replace(/[-_]/g, " ");
|
|
94305
|
+
}
|
|
94306
|
+
}
|
|
94307
|
+
let category;
|
|
94308
|
+
const fmCategory = typeof frontmatter.category === "string" ? frontmatter.category.toLowerCase() : "";
|
|
94309
|
+
if (fmCategory === "git" || fmCategory === "dev" || fmCategory === "sys" || fmCategory === "harness") {
|
|
94310
|
+
category = fmCategory;
|
|
94311
|
+
} else {
|
|
94312
|
+
category = defaultCategory ?? deduceCommandCategory(stem);
|
|
94313
|
+
}
|
|
94314
|
+
const hintRaw = frontmatter["argument-hint"] ?? frontmatter.argumentHint ?? frontmatter.arguments ?? frontmatter.args;
|
|
94315
|
+
const argumentHint = typeof hintRaw === "string" && hintRaw.trim().length > 0 ? hintRaw.trim() : undefined;
|
|
94316
|
+
const roleRaw = frontmatter.role;
|
|
94317
|
+
const role = typeof roleRaw === "string" && roleRaw.trim().length > 0 ? roleRaw.trim() : undefined;
|
|
94318
|
+
const rawTags = frontmatter.tags ?? frontmatter.tag;
|
|
94319
|
+
const tagSet = new Set;
|
|
94320
|
+
if (Array.isArray(rawTags)) {
|
|
94321
|
+
for (const t of rawTags) {
|
|
94322
|
+
if (typeof t === "string" && t.trim().length > 0) {
|
|
94323
|
+
tagSet.add(t.trim().toLowerCase());
|
|
94324
|
+
}
|
|
94325
|
+
}
|
|
94326
|
+
} else if (typeof rawTags === "string" && rawTags.trim().length > 0) {
|
|
94327
|
+
for (const t of rawTags.split(",")) {
|
|
94328
|
+
if (t.trim().length > 0) {
|
|
94329
|
+
tagSet.add(t.trim().toLowerCase());
|
|
94330
|
+
}
|
|
94331
|
+
}
|
|
94332
|
+
}
|
|
94333
|
+
if (Array.isArray(options.tags)) {
|
|
94334
|
+
for (const t of options.tags) {
|
|
94335
|
+
if (t.trim().length > 0) {
|
|
94336
|
+
tagSet.add(t.trim().toLowerCase());
|
|
94337
|
+
}
|
|
94338
|
+
}
|
|
94339
|
+
}
|
|
94340
|
+
if (category) {
|
|
94341
|
+
tagSet.add(category);
|
|
94342
|
+
}
|
|
94343
|
+
if (pluginId) {
|
|
94344
|
+
tagSet.add(pluginId.toLowerCase());
|
|
94345
|
+
} else if (prefix && prefix !== "/") {
|
|
94346
|
+
const clean = prefix.replace(/^[/:]+|[/:]+$/g, "").toLowerCase();
|
|
94347
|
+
if (clean.length > 0) {
|
|
94348
|
+
tagSet.add(clean);
|
|
94349
|
+
}
|
|
94350
|
+
}
|
|
94351
|
+
for (const part of stem.split(/[-_]/)) {
|
|
94352
|
+
const p = part.trim().toLowerCase();
|
|
94353
|
+
if (p.length > 1 && !tagSet.has(p)) {
|
|
94354
|
+
tagSet.add(p);
|
|
94355
|
+
}
|
|
94356
|
+
}
|
|
94357
|
+
const tags = Array.from(tagSet);
|
|
94358
|
+
items.push({
|
|
94359
|
+
name,
|
|
94360
|
+
description,
|
|
94361
|
+
category,
|
|
94362
|
+
argumentHint,
|
|
94363
|
+
tags,
|
|
94364
|
+
role,
|
|
94365
|
+
source
|
|
94366
|
+
});
|
|
94367
|
+
}
|
|
94368
|
+
} catch {}
|
|
94369
|
+
return items;
|
|
94370
|
+
}
|
|
94371
|
+
function discoverEnabledClaudePlugins(options = {}) {
|
|
94372
|
+
const { projectRoot = process.cwd(), claudeDir = join30(homedir9(), ".claude") } = options;
|
|
94373
|
+
const userSettingsPath = join30(claudeDir, "settings.json");
|
|
94374
|
+
const projectSettingsPath = join30(projectRoot, ".claude", "settings.json");
|
|
94375
|
+
const installedPath = join30(claudeDir, "plugins", "installed_plugins.json");
|
|
94376
|
+
let userSettings = {};
|
|
94377
|
+
if (existsSync8(userSettingsPath)) {
|
|
94378
|
+
try {
|
|
94379
|
+
userSettings = JSON.parse(readFileSync10(userSettingsPath, "utf-8"));
|
|
94380
|
+
} catch {}
|
|
94381
|
+
}
|
|
94382
|
+
let projectSettings = {};
|
|
94383
|
+
if (existsSync8(projectSettingsPath)) {
|
|
94384
|
+
try {
|
|
94385
|
+
projectSettings = JSON.parse(readFileSync10(projectSettingsPath, "utf-8"));
|
|
94386
|
+
} catch {}
|
|
94387
|
+
}
|
|
94388
|
+
const enabledPlugins = {
|
|
94389
|
+
...userSettings.enabledPlugins ?? {},
|
|
94390
|
+
...projectSettings.enabledPlugins ?? {}
|
|
94391
|
+
};
|
|
94392
|
+
const extraKnownMarketplaces = {
|
|
94393
|
+
...userSettings.extraKnownMarketplaces ?? {},
|
|
94394
|
+
...projectSettings.extraKnownMarketplaces ?? {}
|
|
94395
|
+
};
|
|
94396
|
+
let installedManifest = { plugins: {} };
|
|
94397
|
+
if (existsSync8(installedPath)) {
|
|
94398
|
+
try {
|
|
94399
|
+
installedManifest = JSON.parse(readFileSync10(installedPath, "utf-8"));
|
|
94400
|
+
} catch {}
|
|
94401
|
+
}
|
|
94402
|
+
const installedPlugins = installedManifest.plugins ?? {};
|
|
94403
|
+
const candidateKeys = new Set;
|
|
94404
|
+
for (const [key2, isEnabled] of Object.entries(enabledPlugins)) {
|
|
94405
|
+
if (isEnabled) {
|
|
94406
|
+
candidateKeys.add(key2);
|
|
94407
|
+
}
|
|
94408
|
+
}
|
|
94409
|
+
if (candidateKeys.size === 0) {
|
|
94410
|
+
for (const key2 of Object.keys(installedPlugins)) {
|
|
94411
|
+
if (enabledPlugins[key2] !== false) {
|
|
94412
|
+
candidateKeys.add(key2);
|
|
94413
|
+
}
|
|
94414
|
+
}
|
|
94415
|
+
}
|
|
94416
|
+
const results = [];
|
|
94417
|
+
for (const pluginKey of candidateKeys) {
|
|
94418
|
+
if (enabledPlugins[pluginKey] === false) {
|
|
94419
|
+
continue;
|
|
94420
|
+
}
|
|
94421
|
+
const atIndex = pluginKey.indexOf("@");
|
|
94422
|
+
const pluginName = atIndex !== -1 ? pluginKey.slice(0, atIndex) : pluginKey;
|
|
94423
|
+
const marketplaceName = atIndex !== -1 ? pluginKey.slice(atIndex + 1) : undefined;
|
|
94424
|
+
let resolvedDir = null;
|
|
94425
|
+
const installs = installedPlugins[pluginKey];
|
|
94426
|
+
if (Array.isArray(installs)) {
|
|
94427
|
+
for (const inst of installs) {
|
|
94428
|
+
if (typeof inst.installPath === "string" && existsSync8(inst.installPath)) {
|
|
94429
|
+
resolvedDir = inst.installPath;
|
|
94430
|
+
break;
|
|
94431
|
+
}
|
|
94432
|
+
}
|
|
94433
|
+
}
|
|
94434
|
+
if (!resolvedDir && marketplaceName && extraKnownMarketplaces[marketplaceName]) {
|
|
94435
|
+
const mSrc = extraKnownMarketplaces[marketplaceName]?.source;
|
|
94436
|
+
if (mSrc?.source === "directory" && typeof mSrc.path === "string") {
|
|
94437
|
+
const cand1 = join30(mSrc.path, "plugins", pluginName);
|
|
94438
|
+
const cand2 = join30(mSrc.path, pluginName);
|
|
94439
|
+
if (existsSync8(cand1)) {
|
|
94440
|
+
resolvedDir = cand1;
|
|
94441
|
+
} else if (existsSync8(cand2)) {
|
|
94442
|
+
resolvedDir = cand2;
|
|
94443
|
+
}
|
|
94444
|
+
}
|
|
94445
|
+
}
|
|
94446
|
+
if (!resolvedDir && marketplaceName) {
|
|
94447
|
+
const cacheDir = join30(claudeDir, "plugins", "cache", marketplaceName, pluginName);
|
|
94448
|
+
if (existsSync8(cacheDir)) {
|
|
94449
|
+
try {
|
|
94450
|
+
const entries = readdirSync7(cacheDir, { withFileTypes: true });
|
|
94451
|
+
const dirs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
94452
|
+
const lastDir = dirs[dirs.length - 1];
|
|
94453
|
+
if (lastDir) {
|
|
94454
|
+
resolvedDir = join30(cacheDir, lastDir);
|
|
94455
|
+
}
|
|
94456
|
+
} catch {}
|
|
94457
|
+
}
|
|
94458
|
+
}
|
|
94459
|
+
if (!resolvedDir && marketplaceName) {
|
|
94460
|
+
const mpDir = join30(claudeDir, "plugins", "marketplaces", marketplaceName, "plugins", pluginName);
|
|
94461
|
+
if (existsSync8(mpDir)) {
|
|
94462
|
+
resolvedDir = mpDir;
|
|
94463
|
+
}
|
|
94464
|
+
}
|
|
94465
|
+
if (!resolvedDir) {
|
|
94466
|
+
const localPlugin = resolve16(projectRoot, "plugins", pluginName);
|
|
94467
|
+
if (existsSync8(localPlugin)) {
|
|
94468
|
+
resolvedDir = localPlugin;
|
|
94469
|
+
}
|
|
94470
|
+
}
|
|
94471
|
+
let commandsDir;
|
|
94472
|
+
if (resolvedDir) {
|
|
94473
|
+
const candidateCommands = join30(resolvedDir, "commands");
|
|
94474
|
+
if (existsSync8(candidateCommands)) {
|
|
94475
|
+
commandsDir = candidateCommands;
|
|
94476
|
+
}
|
|
94477
|
+
}
|
|
94478
|
+
results.push({
|
|
94479
|
+
id: pluginKey,
|
|
94480
|
+
name: pluginName,
|
|
94481
|
+
marketplace: marketplaceName,
|
|
94482
|
+
installPath: resolvedDir ?? undefined,
|
|
94483
|
+
commandsDir
|
|
94484
|
+
});
|
|
94485
|
+
}
|
|
94486
|
+
return results;
|
|
94487
|
+
}
|
|
94488
|
+
function generateSlashCommands(options = {}) {
|
|
94489
|
+
const {
|
|
94490
|
+
projectRoot = process.cwd(),
|
|
94491
|
+
includeBuiltin = true,
|
|
94492
|
+
includePluginSp = true,
|
|
94493
|
+
includeAllPlugins = true,
|
|
94494
|
+
includeProjectClaude = true,
|
|
94495
|
+
includeUserClaude = true,
|
|
94496
|
+
includeUserSpur = true,
|
|
94497
|
+
claudeDir
|
|
94498
|
+
} = options;
|
|
94499
|
+
const commandMap = new Map;
|
|
94500
|
+
if (includeBuiltin) {
|
|
94501
|
+
for (const cmd of getBuiltinClaudeCommands()) {
|
|
94502
|
+
commandMap.set(cmd.name, cmd);
|
|
94503
|
+
}
|
|
94504
|
+
}
|
|
94505
|
+
if (includeAllPlugins) {
|
|
94506
|
+
const plugins = discoverEnabledClaudePlugins({ projectRoot, claudeDir });
|
|
94507
|
+
for (const plugin of plugins) {
|
|
94508
|
+
if (plugin.commandsDir) {
|
|
94509
|
+
const pluginCommands = scanMarkdownCommands(plugin.commandsDir, {
|
|
94510
|
+
prefix: `/${plugin.name}:`,
|
|
94511
|
+
source: `plugin:${plugin.name}`,
|
|
94512
|
+
pluginId: plugin.name,
|
|
94513
|
+
defaultCategory: deduceCommandCategory(plugin.name)
|
|
94514
|
+
});
|
|
94515
|
+
for (const cmd of pluginCommands) {
|
|
94516
|
+
commandMap.set(cmd.name, cmd);
|
|
94517
|
+
}
|
|
94518
|
+
}
|
|
94519
|
+
}
|
|
94520
|
+
}
|
|
94521
|
+
if (includePluginSp) {
|
|
94522
|
+
const spCommandsDir = findPluginsSpCommandsDir(projectRoot) ?? resolve16(projectRoot, "plugins", "sp", "commands");
|
|
94523
|
+
if (existsSync8(spCommandsDir)) {
|
|
94524
|
+
const spCommands = scanMarkdownCommands(spCommandsDir, {
|
|
94525
|
+
prefix: "/sp:",
|
|
94526
|
+
source: "plugin:sp",
|
|
94527
|
+
pluginId: "sp",
|
|
94528
|
+
defaultCategory: "harness"
|
|
94529
|
+
});
|
|
94530
|
+
for (const cmd of spCommands) {
|
|
94531
|
+
commandMap.set(cmd.name, cmd);
|
|
94532
|
+
}
|
|
94533
|
+
}
|
|
94534
|
+
}
|
|
94535
|
+
if (includeProjectClaude) {
|
|
94536
|
+
const projectClaudeDir = findProjectClaudeCommandsDir(projectRoot) ?? resolve16(projectRoot, ".claude", "commands");
|
|
94537
|
+
const projectCommands = scanMarkdownCommands(projectClaudeDir, {
|
|
94538
|
+
prefix: "/",
|
|
94539
|
+
source: "claude:project"
|
|
94540
|
+
});
|
|
94541
|
+
for (const cmd of projectCommands) {
|
|
94542
|
+
commandMap.set(cmd.name, cmd);
|
|
94543
|
+
}
|
|
94544
|
+
}
|
|
94545
|
+
if (includeUserClaude) {
|
|
94546
|
+
const userClaudeDir = join30(homedir9(), ".claude", "commands");
|
|
94547
|
+
const userClaudeCommands = scanMarkdownCommands(userClaudeDir, {
|
|
94548
|
+
prefix: "/",
|
|
94549
|
+
source: "claude:user"
|
|
94550
|
+
});
|
|
94551
|
+
for (const cmd of userClaudeCommands) {
|
|
94552
|
+
commandMap.set(cmd.name, cmd);
|
|
94553
|
+
}
|
|
94554
|
+
}
|
|
94555
|
+
if (includeUserSpur) {
|
|
94556
|
+
const userSpurDir = join30(homedir9(), ".config", "spur", "commands");
|
|
94557
|
+
const userSpurCommands = scanMarkdownCommands(userSpurDir, {
|
|
94558
|
+
prefix: "/sp:",
|
|
94559
|
+
source: "spur:user",
|
|
94560
|
+
pluginId: "sp",
|
|
94561
|
+
defaultCategory: "harness"
|
|
94562
|
+
});
|
|
94563
|
+
for (const cmd of userSpurCommands) {
|
|
94564
|
+
commandMap.set(cmd.name, cmd);
|
|
94565
|
+
}
|
|
94566
|
+
}
|
|
94567
|
+
return Array.from(commandMap.values());
|
|
94568
|
+
}
|
|
94569
|
+
function generateSlashCommandsFile(targetPath = getSlashCommandsFilePath(), options = {}) {
|
|
94570
|
+
const commands = generateSlashCommands(options);
|
|
94571
|
+
const filePayload = {
|
|
94572
|
+
version: 1,
|
|
94573
|
+
updatedAt: new Date().toISOString(),
|
|
94574
|
+
commands
|
|
94575
|
+
};
|
|
94576
|
+
try {
|
|
94577
|
+
const dir = dirname19(targetPath);
|
|
94578
|
+
if (!existsSync8(dir)) {
|
|
94579
|
+
mkdirSync9(dir, { recursive: true });
|
|
94580
|
+
}
|
|
94581
|
+
writeFileSync6(targetPath, JSON.stringify(filePayload, null, 2), "utf-8");
|
|
94582
|
+
} catch {}
|
|
94583
|
+
return {
|
|
94584
|
+
path: targetPath,
|
|
94585
|
+
count: commands.length,
|
|
94586
|
+
commands
|
|
94587
|
+
};
|
|
94588
|
+
}
|
|
94589
|
+
function loadSlashCommands(filePath = getSlashCommandsFilePath(), autoGenerateIfMissing = true, options = {}) {
|
|
94590
|
+
if (!existsSync8(filePath)) {
|
|
94591
|
+
if (autoGenerateIfMissing) {
|
|
94592
|
+
const generated = generateSlashCommandsFile(filePath, options);
|
|
94593
|
+
return generated.commands;
|
|
94594
|
+
}
|
|
94595
|
+
return getBuiltinClaudeCommands();
|
|
94596
|
+
}
|
|
94597
|
+
try {
|
|
94598
|
+
const content = readFileSync10(filePath, "utf-8");
|
|
94599
|
+
const raw = JSON.parse(content);
|
|
94600
|
+
const parsed = slashCommandsFileSchema.safeParse(raw);
|
|
94601
|
+
if (parsed.success && parsed.data.commands.length > 0) {
|
|
94602
|
+
return parsed.data.commands;
|
|
94603
|
+
}
|
|
94604
|
+
} catch {}
|
|
94605
|
+
if (autoGenerateIfMissing) {
|
|
94606
|
+
const generated = generateSlashCommandsFile(filePath, options);
|
|
94607
|
+
return generated.commands;
|
|
94608
|
+
}
|
|
94609
|
+
return getBuiltinClaudeCommands();
|
|
94610
|
+
}
|
|
94611
|
+
|
|
94612
|
+
class SlashCommandsService {
|
|
94613
|
+
filePath;
|
|
94614
|
+
projectRoot;
|
|
94615
|
+
cachedCommands = null;
|
|
94616
|
+
constructor(options = {}) {
|
|
94617
|
+
this.filePath = options.filePath ?? getSlashCommandsFilePath();
|
|
94618
|
+
this.projectRoot = options.projectRoot ?? process.cwd();
|
|
94619
|
+
}
|
|
94620
|
+
getFilePath() {
|
|
94621
|
+
return this.filePath;
|
|
94622
|
+
}
|
|
94623
|
+
getCommands(forceReload = false) {
|
|
94624
|
+
if (!this.cachedCommands || forceReload) {
|
|
94625
|
+
this.cachedCommands = loadSlashCommands(this.filePath, true, {
|
|
94626
|
+
projectRoot: this.projectRoot
|
|
94627
|
+
});
|
|
94628
|
+
}
|
|
94629
|
+
return this.cachedCommands;
|
|
94630
|
+
}
|
|
94631
|
+
generate(targetPath) {
|
|
94632
|
+
const dest = targetPath ?? this.filePath;
|
|
94633
|
+
const result = generateSlashCommandsFile(dest, {
|
|
94634
|
+
projectRoot: this.projectRoot
|
|
94635
|
+
});
|
|
94636
|
+
this.cachedCommands = result.commands;
|
|
94637
|
+
return result;
|
|
94638
|
+
}
|
|
94639
|
+
}
|
|
94640
|
+
var BUILTIN_CLAUDE_COMMANDS;
|
|
94641
|
+
var init_slash_commands_service = __esm(() => {
|
|
94642
|
+
init_src();
|
|
94643
|
+
init_dist2();
|
|
94644
|
+
BUILTIN_CLAUDE_COMMANDS = [
|
|
94645
|
+
{
|
|
94646
|
+
name: "/help",
|
|
94647
|
+
description: "View available commands, tools, and usage hints",
|
|
94648
|
+
category: "sys",
|
|
94649
|
+
argumentHint: "[command]",
|
|
94650
|
+
tags: ["builtin", "sys", "help"],
|
|
94651
|
+
source: "builtin"
|
|
94652
|
+
},
|
|
94653
|
+
{
|
|
94654
|
+
name: "/clear",
|
|
94655
|
+
description: "Clear conversation history and reset context window",
|
|
94656
|
+
category: "sys",
|
|
94657
|
+
tags: ["builtin", "sys", "clear"],
|
|
94658
|
+
source: "builtin"
|
|
94659
|
+
},
|
|
94660
|
+
{
|
|
94661
|
+
name: "/compact",
|
|
94662
|
+
description: "Purge execution trace and condense context window",
|
|
94663
|
+
category: "sys",
|
|
94664
|
+
argumentHint: "[focus]",
|
|
94665
|
+
tags: ["builtin", "sys", "compact", "context"],
|
|
94666
|
+
source: "builtin"
|
|
94667
|
+
},
|
|
94668
|
+
{
|
|
94669
|
+
name: "/cost",
|
|
94670
|
+
description: "Inspect token breakdown and USD expenditure",
|
|
94671
|
+
category: "sys",
|
|
94672
|
+
tags: ["builtin", "sys", "cost", "tokens"],
|
|
94673
|
+
source: "builtin"
|
|
94674
|
+
},
|
|
94675
|
+
{
|
|
94676
|
+
name: "/doctor",
|
|
94677
|
+
description: "Run diagnostic health checks on environment and tools",
|
|
94678
|
+
category: "sys",
|
|
94679
|
+
tags: ["builtin", "sys", "doctor", "health"],
|
|
94680
|
+
source: "builtin"
|
|
94681
|
+
},
|
|
94682
|
+
{
|
|
94683
|
+
name: "/init",
|
|
94684
|
+
description: "Initialize project instructions, settings and guidelines",
|
|
94685
|
+
category: "harness",
|
|
94686
|
+
tags: ["builtin", "harness", "init"],
|
|
94687
|
+
source: "builtin"
|
|
94688
|
+
},
|
|
94689
|
+
{
|
|
94690
|
+
name: "/review",
|
|
94691
|
+
description: "Inspect staged git diff, verify test coverage & lint",
|
|
94692
|
+
category: "git",
|
|
94693
|
+
argumentHint: "[target] [--cached]",
|
|
94694
|
+
tags: ["builtin", "git", "review"],
|
|
94695
|
+
source: "builtin"
|
|
94696
|
+
},
|
|
94697
|
+
{
|
|
94698
|
+
name: "/commit",
|
|
94699
|
+
description: "Draft conventional commit message from staged hunks",
|
|
94700
|
+
category: "git",
|
|
94701
|
+
argumentHint: "[--all] [-m <message>]",
|
|
94702
|
+
tags: ["builtin", "git", "commit"],
|
|
94703
|
+
source: "builtin"
|
|
94704
|
+
},
|
|
94705
|
+
{
|
|
94706
|
+
name: "/terminal",
|
|
94707
|
+
description: "Run isolated bash execution in sandbox mirror",
|
|
94708
|
+
category: "sys",
|
|
94709
|
+
argumentHint: "[command]",
|
|
94710
|
+
tags: ["builtin", "sys", "terminal", "bash"],
|
|
94711
|
+
source: "builtin"
|
|
94712
|
+
},
|
|
94713
|
+
{
|
|
94714
|
+
name: "/revert",
|
|
94715
|
+
description: "Undo last agent file changes or restore git stash",
|
|
94716
|
+
category: "git",
|
|
94717
|
+
argumentHint: "[stash-id | file]",
|
|
94718
|
+
tags: ["builtin", "git", "revert"],
|
|
94719
|
+
source: "builtin"
|
|
94720
|
+
},
|
|
94721
|
+
{
|
|
94722
|
+
name: "/resume",
|
|
94723
|
+
description: "Resume paused background agent run",
|
|
94724
|
+
category: "dev",
|
|
94725
|
+
argumentHint: "[run-id]",
|
|
94726
|
+
tags: ["builtin", "dev", "resume"],
|
|
94727
|
+
source: "builtin"
|
|
94728
|
+
},
|
|
94729
|
+
{
|
|
94730
|
+
name: "/bug",
|
|
94731
|
+
description: "Report a bug or issue with diagnostic transcript",
|
|
94732
|
+
category: "sys",
|
|
94733
|
+
argumentHint: "[description]",
|
|
94734
|
+
tags: ["builtin", "sys", "bug"],
|
|
94735
|
+
source: "builtin"
|
|
94736
|
+
},
|
|
94737
|
+
{
|
|
94738
|
+
name: "/config",
|
|
94739
|
+
description: "View or update local/global configuration settings",
|
|
94740
|
+
category: "sys",
|
|
94741
|
+
argumentHint: "[key] [value]",
|
|
94742
|
+
tags: ["builtin", "sys", "config"],
|
|
94743
|
+
source: "builtin"
|
|
94744
|
+
},
|
|
94745
|
+
{
|
|
94746
|
+
name: "/login",
|
|
94747
|
+
description: "Authenticate account and refresh API credentials",
|
|
94748
|
+
category: "sys",
|
|
94749
|
+
tags: ["builtin", "sys", "login", "auth"],
|
|
94750
|
+
source: "builtin"
|
|
94751
|
+
},
|
|
94752
|
+
{
|
|
94753
|
+
name: "/logout",
|
|
94754
|
+
description: "Sign out of current account and remove stored session",
|
|
94755
|
+
category: "sys",
|
|
94756
|
+
tags: ["builtin", "sys", "logout", "auth"],
|
|
94757
|
+
source: "builtin"
|
|
94758
|
+
}
|
|
94759
|
+
];
|
|
94760
|
+
});
|
|
94761
|
+
|
|
94155
94762
|
// ../../packages/app/src/services/write-slot-service.ts
|
|
94156
94763
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
94157
94764
|
|
|
@@ -94501,7 +95108,7 @@ var init_strategy_runtime = __esm(() => {
|
|
|
94501
95108
|
});
|
|
94502
95109
|
|
|
94503
95110
|
// ../../packages/app/src/services/supervisor-service.ts
|
|
94504
|
-
import { dirname as
|
|
95111
|
+
import { dirname as dirname20 } from "path";
|
|
94505
95112
|
function defaultWrapperArgv(agentId) {
|
|
94506
95113
|
return {
|
|
94507
95114
|
command: process.execPath,
|
|
@@ -94561,7 +95168,7 @@ class SupervisorService {
|
|
|
94561
95168
|
}
|
|
94562
95169
|
if (spec.tags.some((tag) => tag.startsWith("fleet:"))) {
|
|
94563
95170
|
await new FleetService({
|
|
94564
|
-
fs: createNodeFileSystem3(
|
|
95171
|
+
fs: createNodeFileSystem3(dirname20(dirname20(this.configDir)))
|
|
94565
95172
|
}).assertLaunchGroundTruth(spec.workspace);
|
|
94566
95173
|
}
|
|
94567
95174
|
const { command, args } = this.resolveCommand(spec);
|
|
@@ -94651,10 +95258,10 @@ class SupervisorService {
|
|
|
94651
95258
|
proc.handle.kill("SIGTERM");
|
|
94652
95259
|
let timedOut = false;
|
|
94653
95260
|
const exitPromise = proc.handle.exited;
|
|
94654
|
-
const timeout = new Promise((
|
|
95261
|
+
const timeout = new Promise((resolve17) => {
|
|
94655
95262
|
setTimeout(() => {
|
|
94656
95263
|
timedOut = true;
|
|
94657
|
-
|
|
95264
|
+
resolve17();
|
|
94658
95265
|
}, 3000);
|
|
94659
95266
|
});
|
|
94660
95267
|
await Promise.race([exitPromise, timeout]);
|
|
@@ -95054,7 +95661,7 @@ async function* followSystemEventsAfter(getDb, opts) {
|
|
|
95054
95661
|
await sleep3(FOLLOW_POLL_INTERVAL_MS);
|
|
95055
95662
|
}
|
|
95056
95663
|
}
|
|
95057
|
-
var FOLLOW_POLL_INTERVAL_MS = 100, FOLLOW_BATCH_SIZE = 512, sleep3 = (ms) => new Promise((
|
|
95664
|
+
var FOLLOW_POLL_INTERVAL_MS = 100, FOLLOW_BATCH_SIZE = 512, sleep3 = (ms) => new Promise((resolve17) => setTimeout(resolve17, ms));
|
|
95058
95665
|
var init_system_event_follow = __esm(() => {
|
|
95059
95666
|
init_src2();
|
|
95060
95667
|
});
|
|
@@ -95302,7 +95909,7 @@ var init_task_readiness = __esm(() => {
|
|
|
95302
95909
|
});
|
|
95303
95910
|
|
|
95304
95911
|
// ../../packages/app/src/services/task-scaffold.ts
|
|
95305
|
-
import { dirname as
|
|
95912
|
+
import { dirname as dirname21 } from "path";
|
|
95306
95913
|
|
|
95307
95914
|
class TaskScaffoldService {
|
|
95308
95915
|
ctx;
|
|
@@ -95345,7 +95952,7 @@ class TaskScaffoldService {
|
|
|
95345
95952
|
fileExists2 = false;
|
|
95346
95953
|
}
|
|
95347
95954
|
const mergeResult = mergeStubs(existingContent, stubs);
|
|
95348
|
-
const dir =
|
|
95955
|
+
const dir = dirname21(targetFile);
|
|
95349
95956
|
await this.ctx.fs.ensureDir(dir);
|
|
95350
95957
|
if (mergeResult.created > 0 || !fileExists2) {
|
|
95351
95958
|
await this.ctx.fs.writeFile(targetFile, mergeResult.content);
|
|
@@ -95427,7 +96034,7 @@ var init_task_size_precheck = __esm(() => {
|
|
|
95427
96034
|
});
|
|
95428
96035
|
|
|
95429
96036
|
// ../../packages/app/src/services/task-service.ts
|
|
95430
|
-
import { dirname as
|
|
96037
|
+
import { dirname as dirname22, isAbsolute as isAbsolute7, join as join31, relative as relative5 } from "path";
|
|
95431
96038
|
function renderRosterTable(rows) {
|
|
95432
96039
|
const header = `| WBS | Sub-task | Status |
|
|
95433
96040
|
| --- | -------- | ------ |`;
|
|
@@ -95567,7 +96174,8 @@ ${templateBackground}`;
|
|
|
95567
96174
|
...input.featureId !== undefined ? { feature_id: input.featureId } : {},
|
|
95568
96175
|
...input.parentWbs !== undefined ? { parent_wbs: input.parentWbs } : {},
|
|
95569
96176
|
...input.priority !== undefined ? { priority: input.priority } : {},
|
|
95570
|
-
...input.tags !== undefined && input.tags.length > 0 ? { tags: input.tags } : {}
|
|
96177
|
+
...input.tags !== undefined && input.tags.length > 0 ? { tags: input.tags } : {},
|
|
96178
|
+
...input.estimateHours !== undefined ? { estimate_hours: input.estimateHours } : {}
|
|
95571
96179
|
});
|
|
95572
96180
|
let status;
|
|
95573
96181
|
if (input.explicitStatus !== undefined) {
|
|
@@ -95667,7 +96275,8 @@ ${templateBackground}`;
|
|
|
95667
96275
|
done_forced: true,
|
|
95668
96276
|
done_reason: true,
|
|
95669
96277
|
ac_numbering: true,
|
|
95670
|
-
ac_altitude: true
|
|
96278
|
+
ac_altitude: true,
|
|
96279
|
+
estimate_hours: true
|
|
95671
96280
|
};
|
|
95672
96281
|
if (!(key2 in allowed)) {
|
|
95673
96282
|
throw new Error(`Field "${key2}" is not settable via update; allowed: ${Object.keys(allowed).join(", ")}.`);
|
|
@@ -95917,8 +96526,8 @@ ${templateBackground}`;
|
|
|
95917
96526
|
const featureId2 = fm.feature_id ?? fm["feature-id"];
|
|
95918
96527
|
if (!featureId2 || featureId2.length === 0)
|
|
95919
96528
|
return [];
|
|
95920
|
-
const tasksDir =
|
|
95921
|
-
const featuresDir =
|
|
96529
|
+
const tasksDir = dirname22(taskFilePath);
|
|
96530
|
+
const featuresDir = join31(tasksDir, "..", "features");
|
|
95922
96531
|
const featurePath = await (async () => {
|
|
95923
96532
|
try {
|
|
95924
96533
|
for (const name of await this.ctx.fs.readDir(featuresDir)) {
|
|
@@ -96062,7 +96671,8 @@ ${issues}`);
|
|
|
96062
96671
|
plan: item.plan,
|
|
96063
96672
|
acceptanceCriteria: item.acceptance_criteria,
|
|
96064
96673
|
priority: item.priority,
|
|
96065
|
-
tags: item.tags
|
|
96674
|
+
tags: item.tags,
|
|
96675
|
+
estimateHours: item.estimate_hours
|
|
96066
96676
|
});
|
|
96067
96677
|
try {
|
|
96068
96678
|
this.validateCandidateOrThrow(item.name, shape);
|
|
@@ -96229,8 +96839,8 @@ ${block}` : block);
|
|
|
96229
96839
|
return { wbs: hit.wbs, filePath: hit.filePath };
|
|
96230
96840
|
if (opts.strict === true)
|
|
96231
96841
|
return null;
|
|
96232
|
-
const
|
|
96233
|
-
const capturedWbs = /^(\d{4})_.+\.md$/.exec(
|
|
96842
|
+
const basename12 = filePath.split("/").pop() ?? "";
|
|
96843
|
+
const capturedWbs = /^(\d{4})_.+\.md$/.exec(basename12)?.[1];
|
|
96234
96844
|
if (capturedWbs) {
|
|
96235
96845
|
const taskPath = await this.locator.findPathByWbs(capturedWbs);
|
|
96236
96846
|
if (taskPath !== null)
|
|
@@ -96702,8 +97312,8 @@ var init_task_verdict = __esm(() => {
|
|
|
96702
97312
|
});
|
|
96703
97313
|
|
|
96704
97314
|
// ../../packages/app/src/services/token-ledger-service.ts
|
|
96705
|
-
import { closeSync as closeSync3, existsSync as
|
|
96706
|
-
import { join as
|
|
97315
|
+
import { closeSync as closeSync3, existsSync as existsSync9, fstatSync, openSync as openSync3, readSync } from "fs";
|
|
97316
|
+
import { join as join32 } from "path";
|
|
96707
97317
|
function isSparseToolActivity(events2) {
|
|
96708
97318
|
return events2.length === 0 || !events2.some((e) => TOOL_ACTIVITY_TYPES.has(e.type));
|
|
96709
97319
|
}
|
|
@@ -96754,7 +97364,7 @@ function parseLedgerLine(line) {
|
|
|
96754
97364
|
}
|
|
96755
97365
|
}
|
|
96756
97366
|
function tailTokenLedgerFile(filePath, limit, chunkSize = DEFAULT_CHUNK, before) {
|
|
96757
|
-
if (!
|
|
97367
|
+
if (!existsSync9(filePath)) {
|
|
96758
97368
|
return { events: [], truncated: false };
|
|
96759
97369
|
}
|
|
96760
97370
|
const target = limit + 1;
|
|
@@ -96804,7 +97414,7 @@ class TokenLedgerService {
|
|
|
96804
97414
|
ledgerPath;
|
|
96805
97415
|
chunkSize;
|
|
96806
97416
|
constructor(options) {
|
|
96807
|
-
this.ledgerPath = options.ledgerPath ??
|
|
97417
|
+
this.ledgerPath = options.ledgerPath ?? join32(options.cwd, TOKEN_LEDGER_RELATIVE_PATH);
|
|
96808
97418
|
this.chunkSize = options.chunkSize ?? DEFAULT_CHUNK;
|
|
96809
97419
|
}
|
|
96810
97420
|
get path() {
|
|
@@ -96845,12 +97455,12 @@ class TokenLedgerService {
|
|
|
96845
97455
|
var TOOL_ACTIVITY_TYPES, TOKEN_LEDGER_DEFAULT_LIMIT = 200, TOKEN_LEDGER_MAX_LIMIT = 1000, TOKEN_LEDGER_RELATIVE_PATH, DEFAULT_CHUNK;
|
|
96846
97456
|
var init_token_ledger_service = __esm(() => {
|
|
96847
97457
|
TOOL_ACTIVITY_TYPES = new Set(["read", "write", "bash", "grep", "glob"]);
|
|
96848
|
-
TOKEN_LEDGER_RELATIVE_PATH =
|
|
97458
|
+
TOKEN_LEDGER_RELATIVE_PATH = join32(".spur", "context", "token-ledger.jsonl");
|
|
96849
97459
|
DEFAULT_CHUNK = 64 * 1024;
|
|
96850
97460
|
});
|
|
96851
97461
|
|
|
96852
97462
|
// ../../packages/app/src/services/token-ledger-watcher.ts
|
|
96853
|
-
import { closeSync as closeSync4, existsSync as
|
|
97463
|
+
import { closeSync as closeSync4, existsSync as existsSync10, fstatSync as fstatSync2, openSync as openSync4, readSync as readSync2, watch } from "fs";
|
|
96854
97464
|
|
|
96855
97465
|
class TokenLedgerWatcher {
|
|
96856
97466
|
ledgerPath;
|
|
@@ -96871,7 +97481,7 @@ class TokenLedgerWatcher {
|
|
|
96871
97481
|
this.started = true;
|
|
96872
97482
|
this.offset = this.currentSize();
|
|
96873
97483
|
try {
|
|
96874
|
-
const target =
|
|
97484
|
+
const target = existsSync10(this.ledgerPath) ? this.ledgerPath : this.parentDir();
|
|
96875
97485
|
this.watcher = watch(target, () => this.schedulePoll());
|
|
96876
97486
|
} catch {
|
|
96877
97487
|
this.watcher = undefined;
|
|
@@ -96906,7 +97516,7 @@ class TokenLedgerWatcher {
|
|
|
96906
97516
|
return idx > 0 ? this.ledgerPath.slice(0, idx) : ".";
|
|
96907
97517
|
}
|
|
96908
97518
|
currentSize() {
|
|
96909
|
-
if (!
|
|
97519
|
+
if (!existsSync10(this.ledgerPath))
|
|
96910
97520
|
return 0;
|
|
96911
97521
|
try {
|
|
96912
97522
|
const fd = openSync4(this.ledgerPath, "r");
|
|
@@ -96928,7 +97538,7 @@ class TokenLedgerWatcher {
|
|
|
96928
97538
|
}, this.debounceMs);
|
|
96929
97539
|
}
|
|
96930
97540
|
pollNewBytes() {
|
|
96931
|
-
if (!
|
|
97541
|
+
if (!existsSync10(this.ledgerPath)) {
|
|
96932
97542
|
this.offset = 0;
|
|
96933
97543
|
this.carry = "";
|
|
96934
97544
|
return;
|
|
@@ -96973,7 +97583,7 @@ var init_token_ledger_watcher = __esm(() => {
|
|
|
96973
97583
|
});
|
|
96974
97584
|
|
|
96975
97585
|
// ../../packages/app/src/workflow/idea-handoff.ts
|
|
96976
|
-
import { resolve as
|
|
97586
|
+
import { resolve as resolve17 } from "path";
|
|
96977
97587
|
function processEvidence(r) {
|
|
96978
97588
|
return `exit=${r.exitCode ?? "null"}${r.signal ? ` signal=${r.signal}` : ""}: ${r.stderr.trim() || "no stderr"}`;
|
|
96979
97589
|
}
|
|
@@ -96983,12 +97593,12 @@ async function finalizeIdeaHandoff(options) {
|
|
|
96983
97593
|
const executor = options.processExecutor ?? new NodeProcessExecutor3;
|
|
96984
97594
|
const spurBin = options.spurBin ?? "spur";
|
|
96985
97595
|
const { runId, featureId: featureId2 } = options;
|
|
96986
|
-
const runDir =
|
|
96987
|
-
const batchPath =
|
|
96988
|
-
const resultPath =
|
|
96989
|
-
const orderPath =
|
|
96990
|
-
const readyPath =
|
|
96991
|
-
const reportPath =
|
|
97596
|
+
const runDir = resolve17(root, ".spur", "run");
|
|
97597
|
+
const batchPath = resolve17(runDir, `${runId}-idea-task-batch.json`);
|
|
97598
|
+
const resultPath = resolve17(runDir, `${runId}-idea-batch-create-result.json`);
|
|
97599
|
+
const orderPath = resolve17(runDir, `${runId}-idea-task-order.json`);
|
|
97600
|
+
const readyPath = resolve17(runDir, `${runId}-idea-ready.json`);
|
|
97601
|
+
const reportPath = resolve17(runDir, `${runId}-idea-handoff.md`);
|
|
96992
97602
|
const spurSplit = splitLaunchCommand(spurBin, 'idea-handoff "spurBin"');
|
|
96993
97603
|
if ("error" in spurSplit) {
|
|
96994
97604
|
return {
|
|
@@ -97781,7 +98391,7 @@ var init_progress_follow = __esm(() => {
|
|
|
97781
98391
|
});
|
|
97782
98392
|
|
|
97783
98393
|
// ../../packages/app/src/workflow/trace-writer.ts
|
|
97784
|
-
import { dirname as
|
|
98394
|
+
import { dirname as dirname23, join as join33 } from "path";
|
|
97785
98395
|
|
|
97786
98396
|
class WorkflowTraceWriter {
|
|
97787
98397
|
path;
|
|
@@ -97789,7 +98399,7 @@ class WorkflowTraceWriter {
|
|
|
97789
98399
|
pending = Promise.resolve();
|
|
97790
98400
|
constructor(cwd, runId) {
|
|
97791
98401
|
const safeRunId = runId.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
97792
|
-
this.path =
|
|
98402
|
+
this.path = join33(cwd, ".spur", "workflow", `${safeRunId}.jsonl`);
|
|
97793
98403
|
}
|
|
97794
98404
|
attach(bus) {
|
|
97795
98405
|
bus.on("workflow.run.started", (event) => this.enqueue("workflow.run.started", event));
|
|
@@ -97815,7 +98425,7 @@ class WorkflowTraceWriter {
|
|
|
97815
98425
|
`;
|
|
97816
98426
|
this.pending = this.pending.then(async () => {
|
|
97817
98427
|
const fs3 = createNodeFileSystem3();
|
|
97818
|
-
await fs3.ensureDir(
|
|
98428
|
+
await fs3.ensureDir(dirname23(this.path));
|
|
97819
98429
|
await fs3.appendFile(this.path, line);
|
|
97820
98430
|
});
|
|
97821
98431
|
}
|
|
@@ -97910,6 +98520,7 @@ __export(exports_src2, {
|
|
|
97910
98520
|
setPortProbeForTests: () => setPortProbeForTests,
|
|
97911
98521
|
setDetachedServeSpawnForTests: () => setDetachedServeSpawnForTests,
|
|
97912
98522
|
schedulerJobTimeoutEnvName: () => schedulerJobTimeoutEnvName,
|
|
98523
|
+
scanMarkdownCommands: () => scanMarkdownCommands,
|
|
97913
98524
|
satisfies: () => satisfies,
|
|
97914
98525
|
runIdeaHandoffCli: () => runIdeaHandoffCli,
|
|
97915
98526
|
runHistoryReport: () => runHistoryReport,
|
|
@@ -97982,6 +98593,7 @@ __export(exports_src2, {
|
|
|
97982
98593
|
normalizeLegacyTimeoutMs: () => normalizeLegacyTimeoutMs,
|
|
97983
98594
|
materializeRoster: () => materializeRoster,
|
|
97984
98595
|
looksLikeOpaqueId: () => looksLikeOpaqueId,
|
|
98596
|
+
loadSlashCommands: () => loadSlashCommands,
|
|
97985
98597
|
isSystemEventEnvelopeV2: () => isSystemEventEnvelopeV2,
|
|
97986
98598
|
isSchedulerCustomActiveConflict: () => isSchedulerCustomActiveConflict,
|
|
97987
98599
|
isRecordAuthoredReview: () => isRecordAuthoredReview,
|
|
@@ -98001,7 +98613,10 @@ __export(exports_src2, {
|
|
|
98001
98613
|
handleHistoryRefreshJob: () => handleHistoryRefreshJob,
|
|
98002
98614
|
gtdStrategy: () => gtdStrategy,
|
|
98003
98615
|
getLatestSystemEventSequence: () => getLatestSystemEventSequence,
|
|
98616
|
+
getBuiltinClaudeCommands: () => getBuiltinClaudeCommands,
|
|
98004
98617
|
genericSystemEventCatalogEntry: () => genericSystemEventCatalogEntry,
|
|
98618
|
+
generateSlashCommandsFile: () => generateSlashCommandsFile,
|
|
98619
|
+
generateSlashCommands: () => generateSlashCommands,
|
|
98005
98620
|
formatNoopMessage: () => formatNoopMessage,
|
|
98006
98621
|
formatIssue: () => formatIssue,
|
|
98007
98622
|
formatDenialMessage: () => formatDenialMessage,
|
|
@@ -98023,7 +98638,9 @@ __export(exports_src2, {
|
|
|
98023
98638
|
enqueueHistoryRefresh: () => enqueueHistoryRefresh,
|
|
98024
98639
|
encodeRunListCursor: () => encodeRunListCursor,
|
|
98025
98640
|
drainPendingAgentQuotaUpdates: () => drainPendingAgentQuotaUpdates,
|
|
98641
|
+
discoverEnabledClaudePlugins: () => discoverEnabledClaudePlugins,
|
|
98026
98642
|
deriveVerdict: () => deriveVerdict,
|
|
98643
|
+
deduceCommandCategory: () => deduceCommandCategory,
|
|
98027
98644
|
decorateWorkflowEvent: () => decorateWorkflowEvent,
|
|
98028
98645
|
decodeRunListCursor: () => decodeRunListCursor,
|
|
98029
98646
|
decisionKindForGate: () => decisionKindForGate,
|
|
@@ -98086,6 +98703,7 @@ __export(exports_src2, {
|
|
|
98086
98703
|
SystemEventEmitter: () => SystemEventEmitter,
|
|
98087
98704
|
SupervisorService: () => SupervisorService,
|
|
98088
98705
|
StrategyRuntime: () => StrategyRuntime,
|
|
98706
|
+
SlashCommandsService: () => SlashCommandsService,
|
|
98089
98707
|
SectionMutationError: () => SectionMutationError,
|
|
98090
98708
|
SYSTEM_EVENT_UNCATALOGED_WARN_EVENT: () => SYSTEM_EVENT_UNCATALOGED_WARN_EVENT,
|
|
98091
98709
|
SYSTEM_EVENT_STREAMED_NAMES: () => SYSTEM_EVENT_STREAMED_NAMES,
|
|
@@ -98174,6 +98792,7 @@ __export(exports_src2, {
|
|
|
98174
98792
|
CommandGateActionRunner: () => CommandGateActionRunner,
|
|
98175
98793
|
CHILD_KILL_GRACE_MS: () => CHILD_KILL_GRACE_MS,
|
|
98176
98794
|
BusPlanningEventEmitter: () => BusPlanningEventEmitter,
|
|
98795
|
+
BUILTIN_CLAUDE_COMMANDS: () => BUILTIN_CLAUDE_COMMANDS,
|
|
98177
98796
|
AgentService: () => AgentService,
|
|
98178
98797
|
AgentRunActionRunner: () => AgentRunActionRunner,
|
|
98179
98798
|
AgentExecutionLifecycle: () => AgentExecutionLifecycle,
|
|
@@ -98224,6 +98843,7 @@ var init_src3 = __esm(async () => {
|
|
|
98224
98843
|
init_run_session_observer();
|
|
98225
98844
|
init_run_store_service();
|
|
98226
98845
|
init_scheduler_custom_job_service();
|
|
98846
|
+
init_slash_commands_service();
|
|
98227
98847
|
init_strategy_runtime();
|
|
98228
98848
|
init_supervisor_service();
|
|
98229
98849
|
init_system_event_catch_all();
|
|
@@ -106024,10 +106644,10 @@ init_src2();
|
|
|
106024
106644
|
init_dist7();
|
|
106025
106645
|
init_dist5();
|
|
106026
106646
|
await init_src3();
|
|
106027
|
-
import { existsSync as
|
|
106647
|
+
import { existsSync as existsSync11, readFileSync as readFileSync11 } from "fs";
|
|
106028
106648
|
import { rm, writeFile } from "fs/promises";
|
|
106029
106649
|
import { tmpdir as tmpdir3 } from "os";
|
|
106030
|
-
import { dirname as
|
|
106650
|
+
import { dirname as dirname24, join as join34 } from "path";
|
|
106031
106651
|
|
|
106032
106652
|
// src/colors.ts
|
|
106033
106653
|
var CODES = {
|
|
@@ -106992,9 +107612,9 @@ init_src2();
|
|
|
106992
107612
|
await init_src3();
|
|
106993
107613
|
|
|
106994
107614
|
// src/workflow/resolve-spur-bin.ts
|
|
106995
|
-
import { basename as
|
|
107615
|
+
import { basename as basename12 } from "path";
|
|
106996
107616
|
function resolveSpurBinFrom(launch) {
|
|
106997
|
-
const runtime =
|
|
107617
|
+
const runtime = basename12(launch.execPath).toLowerCase().replace(/\.exe$/, "");
|
|
106998
107618
|
const isJsRuntime = runtime === "bun" || runtime === "node";
|
|
106999
107619
|
if (isJsRuntime && launch.mainModule) {
|
|
107000
107620
|
return `${launch.execPath} ${launch.mainModule}`;
|
|
@@ -107252,7 +107872,7 @@ ${result.content}`);
|
|
|
107252
107872
|
'Sections replace, with one exception: `--section "Q&A"` APPENDS a timestamped',
|
|
107253
107873
|
"`#### Q&A entry \u2014 <ISO>` block. Start the body with `<!-- qa:replace -->` to replace it wholesale."
|
|
107254
107874
|
].join(`
|
|
107255
|
-
`)).option(...SHARED_OPTIONS.section).option(...SHARED_OPTIONS.fromFile).option(...SHARED_OPTIONS.featureFrontmatter).option(...SHARED_OPTIONS.prioritySet).option("--ac-numbering <mode>", "Set the ac_numbering frontmatter field (task-local) \u2014 opts the task into the Requirements\u2194AC coverage check").option("--ac-altitude <mode>", "Set the ac_altitude frontmatter field. Valid: `graduating` (default; feature-AC subset rule enforced) or `task-local` (skip the DD-09 subset rule \u2014 task scenarios are intentionally not feature ship criteria). Mirrors the L1 schema enum (packages/domain/src/planning/schema.ts:304).").option("--no-lifecycle", "Suppress lifecycle workflow run creation (use during pipeline runs to avoid orphaned lifecycle runs)").option("--assignee <spec-id>", "Set the assignee frontmatter field to an agent spec id").option("--force-done", "Allow transitioning to `done` even when the verify verdict is not PASS; records an override (task 0292). Waives the verdict only \u2014 the FSM path still applies, so from an earlier status walk the hops first: `todo` \u2192 `wip` \u2192 `testing` \u2192 `done` (each hop runs the structural `spur task check`)").option("--reason <text>", "Rationale for a forced-done override (paired with --force-done; persisted as done_reason)").option("--verdict-dir <path>", "Directory holding <wbs>-verdict.json artifacts (default: .spur/run)").option(...SHARED_OPTIONS.folderTasks).option(...SHARED_OPTIONS.json).option(...SHARED_OPTIONS.jsonEnvelope).action(async (wbs, status, options) => {
|
|
107875
|
+
`)).option(...SHARED_OPTIONS.section).option(...SHARED_OPTIONS.fromFile).option(...SHARED_OPTIONS.featureFrontmatter).option(...SHARED_OPTIONS.prioritySet).option("--ac-numbering <mode>", "Set the ac_numbering frontmatter field (task-local) \u2014 opts the task into the Requirements\u2194AC coverage check").option("--ac-altitude <mode>", "Set the ac_altitude frontmatter field. Valid: `graduating` (default; feature-AC subset rule enforced) or `task-local` (skip the DD-09 subset rule \u2014 task scenarios are intentionally not feature ship criteria). Mirrors the L1 schema enum (packages/domain/src/planning/schema.ts:304).").option("--estimate-hours <n>", "Set the estimate_hours frontmatter field (positive number) \u2014 decomposition size estimate; the inline pipeline driver dispatches no subagent at/below its floor").option("--no-lifecycle", "Suppress lifecycle workflow run creation (use during pipeline runs to avoid orphaned lifecycle runs)").option("--assignee <spec-id>", "Set the assignee frontmatter field to an agent spec id").option("--force-done", "Allow transitioning to `done` even when the verify verdict is not PASS; records an override (task 0292). Waives the verdict only \u2014 the FSM path still applies, so from an earlier status walk the hops first: `todo` \u2192 `wip` \u2192 `testing` \u2192 `done` (each hop runs the structural `spur task check`)").option("--reason <text>", "Rationale for a forced-done override (paired with --force-done; persisted as done_reason)").option("--verdict-dir <path>", "Directory holding <wbs>-verdict.json artifacts (default: .spur/run)").option(...SHARED_OPTIONS.folderTasks).option(...SHARED_OPTIONS.json).option(...SHARED_OPTIONS.jsonEnvelope).action(async (wbs, status, options) => {
|
|
107256
107876
|
const svc = await makeService(context4, options.folder, options.lifecycle === false);
|
|
107257
107877
|
try {
|
|
107258
107878
|
if (options.assignee !== undefined) {
|
|
@@ -107290,9 +107910,9 @@ ${result.content}`);
|
|
|
107290
107910
|
}
|
|
107291
107911
|
context4.output.write(`Updated section '${options.section}' in task ${result.ref.id}`);
|
|
107292
107912
|
}
|
|
107293
|
-
} else if (options.feature !== undefined || options.priority !== undefined || options.acNumbering !== undefined || options.acAltitude !== undefined) {
|
|
107294
|
-
const key2 = options.feature !== undefined ? "feature_id" : options.priority !== undefined ? "priority" : options.acNumbering !== undefined ? "ac_numbering" : "ac_altitude";
|
|
107295
|
-
const value = options.feature ?? options.priority ?? options.acNumbering ?? options.acAltitude ?? "";
|
|
107913
|
+
} else if (options.feature !== undefined || options.priority !== undefined || options.acNumbering !== undefined || options.acAltitude !== undefined || options.estimateHours !== undefined) {
|
|
107914
|
+
const key2 = options.feature !== undefined ? "feature_id" : options.priority !== undefined ? "priority" : options.acNumbering !== undefined ? "ac_numbering" : options.acAltitude !== undefined ? "ac_altitude" : "estimate_hours";
|
|
107915
|
+
const value = options.feature ?? options.priority ?? options.acNumbering ?? options.acAltitude ?? options.estimateHours ?? "";
|
|
107296
107916
|
const result = await svc.updateField(wbs, key2, value);
|
|
107297
107917
|
if (options.json) {
|
|
107298
107918
|
context4.output.write(toEnvelopeJson(result, { enveloped: options.jsonEnvelope }));
|
|
@@ -107322,7 +107942,7 @@ ${result.content}`);
|
|
|
107322
107942
|
let forcedDoneVerdict;
|
|
107323
107943
|
if (status === "done") {
|
|
107324
107944
|
const current = await svc.show(wbs);
|
|
107325
|
-
const verdictDir = options.verdictDir ??
|
|
107945
|
+
const verdictDir = options.verdictDir ?? join34(context4.cwd, ".spur", "run");
|
|
107326
107946
|
const loaded = await readVerdictArtifact(context4.fs, verdictDir, wbs);
|
|
107327
107947
|
const guardOutcome = evaluateDoneTransition({
|
|
107328
107948
|
wbs,
|
|
@@ -107384,7 +108004,7 @@ ${result.content}`);
|
|
|
107384
108004
|
}
|
|
107385
108005
|
}
|
|
107386
108006
|
} else {
|
|
107387
|
-
context4.output.error("Either <status>, --section/--from-file, or --feature/--priority is required");
|
|
108007
|
+
context4.output.error("Either <status>, --section/--from-file, or a field flag (--feature/--priority/--ac-numbering/--ac-altitude/--estimate-hours) is required");
|
|
107388
108008
|
context4.setExitCode(2);
|
|
107389
108009
|
}
|
|
107390
108010
|
} catch (err) {
|
|
@@ -107558,7 +108178,7 @@ ${result.content}`);
|
|
|
107558
108178
|
dryRun,
|
|
107559
108179
|
projectRoot: context4.cwd,
|
|
107560
108180
|
write: async (filePath, wbs, section, newBody) => {
|
|
107561
|
-
const ref = { kind: "task", id: wbs, filePath, folder:
|
|
108181
|
+
const ref = { kind: "task", id: wbs, filePath, folder: dirname24(filePath) };
|
|
107562
108182
|
const ws = new PlanningWriteService({ fs: context4.fs, emitter: makePlanningEmitter(context4) });
|
|
107563
108183
|
await ws.updateSection(ref, section, newBody);
|
|
107564
108184
|
}
|
|
@@ -107632,7 +108252,7 @@ ${result.content}`);
|
|
|
107632
108252
|
agentSelector: options.agent,
|
|
107633
108253
|
cwd: context4.cwd
|
|
107634
108254
|
});
|
|
107635
|
-
tmpPath =
|
|
108255
|
+
tmpPath = join34(tmpdir3(), `spur-ready-batch-${Date.now()}-${Math.random().toString(36).slice(2, 8)}.json`);
|
|
107636
108256
|
await writeFile(tmpPath, `${JSON.stringify(prepared.items, null, 2)}
|
|
107637
108257
|
`);
|
|
107638
108258
|
batchFile = tmpPath;
|
|
@@ -108065,15 +108685,15 @@ function loadTemplateBodies(projectRoot, variant) {
|
|
|
108065
108685
|
if (cached2 !== undefined)
|
|
108066
108686
|
return cached2;
|
|
108067
108687
|
let bodies = {};
|
|
108068
|
-
const localPath =
|
|
108069
|
-
if (
|
|
108070
|
-
bodies = extractTemplateBodies(
|
|
108688
|
+
const localPath = join34(projectRoot, ".spur", "tasks", "templates", `${variant}.md`);
|
|
108689
|
+
if (existsSync11(localPath)) {
|
|
108690
|
+
bodies = extractTemplateBodies(readFileSync11(localPath, "utf8"));
|
|
108071
108691
|
} else {
|
|
108072
108692
|
const root = bundledConfigRoot();
|
|
108073
108693
|
if (root !== null) {
|
|
108074
|
-
const templatePath =
|
|
108075
|
-
if (
|
|
108076
|
-
bodies = extractTemplateBodies(
|
|
108694
|
+
const templatePath = join34(root, "templates", "task", `${variant}.md`);
|
|
108695
|
+
if (existsSync11(templatePath)) {
|
|
108696
|
+
bodies = extractTemplateBodies(readFileSync11(templatePath, "utf8"));
|
|
108077
108697
|
}
|
|
108078
108698
|
}
|
|
108079
108699
|
}
|
|
@@ -108129,7 +108749,7 @@ async function loadSectionMatrixUncached(projectRoot) {
|
|
|
108129
108749
|
}
|
|
108130
108750
|
const root = bundledConfigRoot();
|
|
108131
108751
|
if (root !== null) {
|
|
108132
|
-
const matrixPath =
|
|
108752
|
+
const matrixPath = join34(root, "tasks", "section-matrix.yaml");
|
|
108133
108753
|
if (await fs3.exists(matrixPath)) {
|
|
108134
108754
|
const data = await loadStructuredSpurConfig(matrixPath, {
|
|
108135
108755
|
validateJsonSchema: true,
|
|
@@ -108140,7 +108760,7 @@ async function loadSectionMatrixUncached(projectRoot) {
|
|
|
108140
108760
|
}
|
|
108141
108761
|
throw new Error(`no canonical section-matrix found for task section authority (F92 R1); tried:
|
|
108142
108762
|
` + ` - ${localPath}
|
|
108143
|
-
` + (root !== null ? ` - ${
|
|
108763
|
+
` + (root !== null ? ` - ${join34(root, "tasks", "section-matrix.yaml")}
|
|
108144
108764
|
` : "") + "copy/generate section-matrix.yaml from the canonical build-time matrix asset (repo `config` `tasks` tree) into one of those paths");
|
|
108145
108765
|
}
|
|
108146
108766
|
|
|
@@ -108518,15 +109138,15 @@ function parseTimeout(raw) {
|
|
|
108518
109138
|
return n3;
|
|
108519
109139
|
}
|
|
108520
109140
|
function loopSleep(ms, signal) {
|
|
108521
|
-
return new Promise((
|
|
109141
|
+
return new Promise((resolve18) => {
|
|
108522
109142
|
if (signal?.aborted) {
|
|
108523
|
-
|
|
109143
|
+
resolve18();
|
|
108524
109144
|
return;
|
|
108525
109145
|
}
|
|
108526
|
-
const timer = setTimeout(
|
|
109146
|
+
const timer = setTimeout(resolve18, ms);
|
|
108527
109147
|
signal?.addEventListener("abort", () => {
|
|
108528
109148
|
clearTimeout(timer);
|
|
108529
|
-
|
|
109149
|
+
resolve18();
|
|
108530
109150
|
}, { once: true });
|
|
108531
109151
|
});
|
|
108532
109152
|
}
|
|
@@ -108846,13 +109466,13 @@ function waitFail(context4, options, code, message) {
|
|
|
108846
109466
|
// src/release-ops.ts
|
|
108847
109467
|
init_src();
|
|
108848
109468
|
init_dist5();
|
|
108849
|
-
import { existsSync as
|
|
108850
|
-
import { join as
|
|
109469
|
+
import { existsSync as existsSync13, readdirSync as readdirSync8, readFileSync as readFileSync12 } from "fs";
|
|
109470
|
+
import { join as join36 } from "path";
|
|
108851
109471
|
|
|
108852
109472
|
// src/version-carriers.ts
|
|
108853
109473
|
init_zod();
|
|
108854
|
-
import { existsSync as
|
|
108855
|
-
import { join as
|
|
109474
|
+
import { existsSync as existsSync12 } from "fs";
|
|
109475
|
+
import { join as join35 } from "path";
|
|
108856
109476
|
var defaultLiteralProbe = { file: "src/config.ts", identifier: "binaryVersion" };
|
|
108857
109477
|
var registry2 = new Map;
|
|
108858
109478
|
function registerCarrierType(type, def) {
|
|
@@ -108888,7 +109508,7 @@ function findLiteralProbe(carriers, packageDir) {
|
|
|
108888
109508
|
return;
|
|
108889
109509
|
}
|
|
108890
109510
|
async function readJson(path9) {
|
|
108891
|
-
if (!
|
|
109511
|
+
if (!existsSync12(path9))
|
|
108892
109512
|
return null;
|
|
108893
109513
|
try {
|
|
108894
109514
|
return JSON.parse(await Bun.file(path9).text());
|
|
@@ -108904,7 +109524,7 @@ registerCarrierType("plugin-manifest", {
|
|
|
108904
109524
|
schema: PluginManifestCarrierSchema,
|
|
108905
109525
|
async syncRepoWide(carrier, { repoRoot }, version3, staged, output2) {
|
|
108906
109526
|
for (const rel of carrier.paths) {
|
|
108907
|
-
const absPath =
|
|
109527
|
+
const absPath = join35(repoRoot, rel);
|
|
108908
109528
|
const manifest = await readJson(absPath);
|
|
108909
109529
|
if (manifest === null) {
|
|
108910
109530
|
output2.write(` \u26A0 ${rel}: not found or malformed \u2014 skipping`);
|
|
@@ -108988,7 +109608,7 @@ async function readJson2(path9) {
|
|
|
108988
109608
|
function rootWorkspaceGlobs(repoRoot) {
|
|
108989
109609
|
let root;
|
|
108990
109610
|
try {
|
|
108991
|
-
root = JSON.parse(
|
|
109611
|
+
root = JSON.parse(readFileSync12(join36(repoRoot, "package.json"), "utf8"));
|
|
108992
109612
|
} catch {
|
|
108993
109613
|
return [];
|
|
108994
109614
|
}
|
|
@@ -109005,11 +109625,11 @@ function workspaceDirs(repoRoot) {
|
|
|
109005
109625
|
if (!glob.endsWith("/*"))
|
|
109006
109626
|
continue;
|
|
109007
109627
|
const parent = glob.slice(0, -2);
|
|
109008
|
-
const parentAbs =
|
|
109009
|
-
if (!
|
|
109628
|
+
const parentAbs = join36(repoRoot, parent);
|
|
109629
|
+
if (!existsSync13(parentAbs))
|
|
109010
109630
|
continue;
|
|
109011
|
-
for (const entry of
|
|
109012
|
-
if (entry.isDirectory() &&
|
|
109631
|
+
for (const entry of readdirSync8(parentAbs, { withFileTypes: true })) {
|
|
109632
|
+
if (entry.isDirectory() && existsSync13(join36(parentAbs, entry.name, "package.json"))) {
|
|
109013
109633
|
dirs.push(`${parent}/${entry.name}`);
|
|
109014
109634
|
}
|
|
109015
109635
|
}
|
|
@@ -109017,14 +109637,14 @@ function workspaceDirs(repoRoot) {
|
|
|
109017
109637
|
return dirs;
|
|
109018
109638
|
}
|
|
109019
109639
|
async function releaseContext(repoRoot, bumpVerConfig = resolveBuilderBumpVerConfig(null)) {
|
|
109020
|
-
const rootManifest = await readJson2(
|
|
109640
|
+
const rootManifest = await readJson2(join36(repoRoot, "package.json"));
|
|
109021
109641
|
if (rootManifest === null) {
|
|
109022
109642
|
throw new Error(`no package.json found at ${repoRoot} \u2014 builder operates on a package workspace root.`);
|
|
109023
109643
|
}
|
|
109024
109644
|
const rootName = typeof rootManifest.name === "string" ? rootManifest.name : "root";
|
|
109025
109645
|
const workspaces2 = [];
|
|
109026
109646
|
for (const dir of workspaceDirs(repoRoot)) {
|
|
109027
|
-
const manifest = await readJson2(
|
|
109647
|
+
const manifest = await readJson2(join36(repoRoot, dir, "package.json"));
|
|
109028
109648
|
if (manifest === null)
|
|
109029
109649
|
continue;
|
|
109030
109650
|
workspaces2.push({
|
|
@@ -109039,8 +109659,8 @@ async function releaseContext(repoRoot, bumpVerConfig = resolveBuilderBumpVerCon
|
|
|
109039
109659
|
const packages = new Map;
|
|
109040
109660
|
for (const ws of workspaces2) {
|
|
109041
109661
|
const probe = findLiteralProbe(versionCarriers, ws.dir) ?? defaultLiteralProbe;
|
|
109042
|
-
const literalProbe =
|
|
109043
|
-
const versionSourceFile =
|
|
109662
|
+
const literalProbe = join36(ws.dir, probe.file);
|
|
109663
|
+
const versionSourceFile = existsSync13(join36(repoRoot, literalProbe)) ? literalProbe : undefined;
|
|
109044
109664
|
packages.set(shortName(ws.name), {
|
|
109045
109665
|
packageDir: ws.dir,
|
|
109046
109666
|
packageName: ws.name,
|
|
@@ -109078,7 +109698,7 @@ async function updateWorkspacePins(ctx, pkgName, oldVersion, newVersion, output2
|
|
|
109078
109698
|
const changed = [];
|
|
109079
109699
|
for (const dir of workspaceDirs(ctx.repoRoot)) {
|
|
109080
109700
|
const relPath = `${dir}/package.json`;
|
|
109081
|
-
const manifest = await readJson2(
|
|
109701
|
+
const manifest = await readJson2(join36(ctx.repoRoot, relPath));
|
|
109082
109702
|
if (manifest === null)
|
|
109083
109703
|
continue;
|
|
109084
109704
|
let dirty = false;
|
|
@@ -109095,7 +109715,7 @@ async function updateWorkspacePins(ctx, pkgName, oldVersion, newVersion, output2
|
|
|
109095
109715
|
}
|
|
109096
109716
|
}
|
|
109097
109717
|
if (dirty) {
|
|
109098
|
-
await Bun.write(
|
|
109718
|
+
await Bun.write(join36(ctx.repoRoot, relPath), `${JSON.stringify(manifest, null, 4)}
|
|
109099
109719
|
`);
|
|
109100
109720
|
output2.write(` \u21B3 ${relPath}: ${pkgName} workspace pin ${oldVersion} \u2192 ${newVersion}`);
|
|
109101
109721
|
changed.push(relPath);
|
|
@@ -109104,7 +109724,7 @@ async function updateWorkspacePins(ctx, pkgName, oldVersion, newVersion, output2
|
|
|
109104
109724
|
return changed;
|
|
109105
109725
|
}
|
|
109106
109726
|
async function updateVersionSourceFile(ctx, filePath, previous, next, output2, identifier = "binaryVersion") {
|
|
109107
|
-
const absPath =
|
|
109727
|
+
const absPath = join36(ctx.repoRoot, filePath);
|
|
109108
109728
|
const content = await Bun.file(absPath).text();
|
|
109109
109729
|
const pattern = new RegExp(`(${identifier.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}:\\s*['"])\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z-.]+)?(?:\\+[0-9A-Za-z-.]+)?(['"])`);
|
|
109110
109730
|
const updated = content.replace(pattern, `$1${next}$2`);
|
|
@@ -109122,7 +109742,7 @@ async function syncExtraCarriers(ctx, version3, staged, output2) {
|
|
|
109122
109742
|
}
|
|
109123
109743
|
}
|
|
109124
109744
|
async function syncMarketplaceAndPlugins(ctx, version3, staged, output2) {
|
|
109125
|
-
const marketplacePath =
|
|
109745
|
+
const marketplacePath = join36(ctx.repoRoot, ".claude-plugin", "marketplace.json");
|
|
109126
109746
|
const marketplaceFile = Bun.file(marketplacePath);
|
|
109127
109747
|
if (!await marketplaceFile.exists())
|
|
109128
109748
|
return;
|
|
@@ -109147,7 +109767,7 @@ async function syncMarketplaceAndPlugins(ctx, version3, staged, output2) {
|
|
|
109147
109767
|
output2.write(`Bumped marketplace plugins to ${version3}`);
|
|
109148
109768
|
}
|
|
109149
109769
|
for (const entry of plugins) {
|
|
109150
|
-
const pluginJsonPath =
|
|
109770
|
+
const pluginJsonPath = join36(ctx.repoRoot, entry.source, "plugin.json");
|
|
109151
109771
|
const pluginJson = await readJson2(pluginJsonPath);
|
|
109152
109772
|
if (pluginJson === null) {
|
|
109153
109773
|
output2.write(` \u26A0 plugin.json not found at ${entry.source}/plugin.json \u2014 skipping`);
|
|
@@ -109200,7 +109820,7 @@ async function bumpVersion(ctx, config4, version3, options, output2) {
|
|
|
109200
109820
|
if (await npmViewVersion(ctx.repoRoot, config4.packageName, version3)) {
|
|
109201
109821
|
throw new Error(`${config4.packageName}@${version3} is already published on npm. Use a new version.`);
|
|
109202
109822
|
}
|
|
109203
|
-
const manifestPath =
|
|
109823
|
+
const manifestPath = join36(ctx.repoRoot, config4.packageDir, "package.json");
|
|
109204
109824
|
const manifest = await readJson2(manifestPath);
|
|
109205
109825
|
if (manifest === null)
|
|
109206
109826
|
throw new Error(`missing or malformed manifest at ${config4.packageDir}/package.json`);
|
|
@@ -109218,8 +109838,8 @@ async function bumpVersion(ctx, config4, version3, options, output2) {
|
|
|
109218
109838
|
staged.push(...await updateWorkspacePins(ctx, config4.packageName, previous, version3, output2));
|
|
109219
109839
|
await syncMarketplaceAndPlugins(ctx, version3, staged, output2);
|
|
109220
109840
|
await syncExtraCarriers(ctx, version3, staged, output2);
|
|
109221
|
-
const lockPath =
|
|
109222
|
-
if (
|
|
109841
|
+
const lockPath = join36(ctx.repoRoot, "bun.lock");
|
|
109842
|
+
if (existsSync13(lockPath) && Bun.file(lockPath).size > 0)
|
|
109223
109843
|
staged.push("bun.lock");
|
|
109224
109844
|
await git2(ctx.repoRoot, ["add", ...staged]);
|
|
109225
109845
|
const message = `${config4.releaseCommitType}(${config4.releaseCommitScope}): ${config4.releaseCommitSubject(version3)}`;
|
|
@@ -109274,7 +109894,7 @@ async function bumpAll(ctx, version3, options, output2) {
|
|
|
109274
109894
|
}
|
|
109275
109895
|
const staged = [];
|
|
109276
109896
|
for (const config4 of configs) {
|
|
109277
|
-
const manifestPath =
|
|
109897
|
+
const manifestPath = join36(ctx.repoRoot, config4.packageDir, "package.json");
|
|
109278
109898
|
const manifest = await readJson2(manifestPath);
|
|
109279
109899
|
if (manifest === null) {
|
|
109280
109900
|
throw new Error(`missing or malformed manifest at ${config4.packageDir}/package.json`);
|
|
@@ -109294,8 +109914,8 @@ async function bumpAll(ctx, version3, options, output2) {
|
|
|
109294
109914
|
}
|
|
109295
109915
|
await syncMarketplaceAndPlugins(ctx, version3, staged, output2);
|
|
109296
109916
|
await syncExtraCarriers(ctx, version3, staged, output2);
|
|
109297
|
-
const lockPath =
|
|
109298
|
-
if (
|
|
109917
|
+
const lockPath = join36(ctx.repoRoot, "bun.lock");
|
|
109918
|
+
if (existsSync13(lockPath) && Bun.file(lockPath).size > 0)
|
|
109299
109919
|
staged.push("bun.lock");
|
|
109300
109920
|
await git2(ctx.repoRoot, ["add", ...staged]);
|
|
109301
109921
|
const shortNames = configs.map((c3) => c3.packageName.split("/").pop() ?? c3.packageName).join(" + ");
|
|
@@ -109900,7 +110520,7 @@ import { createRequire } from "module";
|
|
|
109900
110520
|
var CLI_CONFIG = {
|
|
109901
110521
|
binaryName: "spur",
|
|
109902
110522
|
binaryLabel: "spur",
|
|
109903
|
-
binaryVersion: "0.3.
|
|
110523
|
+
binaryVersion: "0.3.85",
|
|
109904
110524
|
configDir: ".spur",
|
|
109905
110525
|
configFile: ".spur/config.yaml",
|
|
109906
110526
|
databaseFile: ".spur/spur.db"
|
|
@@ -110311,8 +110931,8 @@ init_src();
|
|
|
110311
110931
|
init_loader();
|
|
110312
110932
|
init_src2();
|
|
110313
110933
|
init_dist10();
|
|
110314
|
-
import { homedir as
|
|
110315
|
-
import { join as
|
|
110934
|
+
import { homedir as homedir10 } from "os";
|
|
110935
|
+
import { join as join37, resolve as resolve18 } from "path";
|
|
110316
110936
|
|
|
110317
110937
|
// src/config/scaffold-manifest.ts
|
|
110318
110938
|
var SCAFFOLD_MANIFEST = [
|
|
@@ -110341,8 +110961,8 @@ var SCAFFOLD_MANIFEST = [
|
|
|
110341
110961
|
];
|
|
110342
110962
|
|
|
110343
110963
|
// src/commands/init.ts
|
|
110344
|
-
var GLOBAL_CONFIG_DIR =
|
|
110345
|
-
var GLOBAL_RULES_DIR2 =
|
|
110964
|
+
var GLOBAL_CONFIG_DIR = join37(".config", "spur");
|
|
110965
|
+
var GLOBAL_RULES_DIR2 = join37(GLOBAL_CONFIG_DIR, "rules");
|
|
110346
110966
|
var GLOBAL_CONFIG_EXAMPLE = BUNDLED_GLOBAL_CONFIG;
|
|
110347
110967
|
var GLOBAL_CONFIG_FILE2 = "config.yaml";
|
|
110348
110968
|
var INDEXED_CONTEXT_MARKER = "## Indexed context";
|
|
@@ -110373,7 +110993,7 @@ If \`.spur/context/\` is absent, proceed normally. Never block work on its absen
|
|
|
110373
110993
|
`;
|
|
110374
110994
|
function globalRulesRoot(context4) {
|
|
110375
110995
|
const override = context4.env.SPUR_GLOBAL_RULES_DIR;
|
|
110376
|
-
return override !== undefined && override.length > 0 ?
|
|
110996
|
+
return override !== undefined && override.length > 0 ? resolve18(context4.cwd, override) : join37(homedir10(), GLOBAL_RULES_DIR2);
|
|
110377
110997
|
}
|
|
110378
110998
|
async function writeIfNew(context4, path9, content, force, result) {
|
|
110379
110999
|
if (!force && await context4.fs.exists(path9)) {
|
|
@@ -110390,18 +111010,18 @@ async function seedGlobalRules(context4) {
|
|
|
110390
111010
|
const target = globalRulesRoot(context4);
|
|
110391
111011
|
let written = 0;
|
|
110392
111012
|
for (const relPath of await listBundledRuleFiles()) {
|
|
110393
|
-
const destination =
|
|
111013
|
+
const destination = join37(target, relPath);
|
|
110394
111014
|
if (await context4.fs.exists(destination))
|
|
110395
111015
|
continue;
|
|
110396
|
-
await context4.fs.ensureDir(
|
|
110397
|
-
await context4.fs.writeFile(destination, await context4.fs.readFile(
|
|
111016
|
+
await context4.fs.ensureDir(join37(target, ...relPath.split("/").slice(0, -1)));
|
|
111017
|
+
await context4.fs.writeFile(destination, await context4.fs.readFile(join37(source, relPath)));
|
|
110398
111018
|
written += 1;
|
|
110399
111019
|
}
|
|
110400
111020
|
return written;
|
|
110401
111021
|
}
|
|
110402
111022
|
function resolveGlobalConfigDir(context4) {
|
|
110403
111023
|
const globalOverride = context4.env.SPUR_GLOBAL_RULES_DIR;
|
|
110404
|
-
return globalOverride !== undefined && globalOverride.length > 0 ?
|
|
111024
|
+
return globalOverride !== undefined && globalOverride.length > 0 ? resolve18(context4.cwd, globalOverride) : join37(homedir10(), GLOBAL_CONFIG_DIR);
|
|
110405
111025
|
}
|
|
110406
111026
|
async function seedGlobalConfig(context4) {
|
|
110407
111027
|
const source = bundledConfigRoot();
|
|
@@ -110412,15 +111032,15 @@ async function seedGlobalConfig(context4) {
|
|
|
110412
111032
|
for (const relPath of listBundledConfigFiles()) {
|
|
110413
111033
|
if (relPath === GLOBAL_CONFIG_EXAMPLE)
|
|
110414
111034
|
continue;
|
|
110415
|
-
const destination =
|
|
111035
|
+
const destination = join37(target, relPath);
|
|
110416
111036
|
if (await context4.fs.exists(destination))
|
|
110417
111037
|
continue;
|
|
110418
|
-
await context4.fs.ensureDir(
|
|
110419
|
-
await context4.fs.writeFile(destination, await context4.fs.readFile(
|
|
111038
|
+
await context4.fs.ensureDir(join37(target, ...relPath.split("/").slice(0, -1)));
|
|
111039
|
+
await context4.fs.writeFile(destination, await context4.fs.readFile(join37(source, relPath)));
|
|
110420
111040
|
written += 1;
|
|
110421
111041
|
}
|
|
110422
|
-
const examplePath =
|
|
110423
|
-
const globalConfigPath =
|
|
111042
|
+
const examplePath = join37(source, GLOBAL_CONFIG_EXAMPLE);
|
|
111043
|
+
const globalConfigPath = join37(target, GLOBAL_CONFIG_FILE2);
|
|
110424
111044
|
if (await context4.fs.exists(examplePath) && !await context4.fs.exists(globalConfigPath)) {
|
|
110425
111045
|
await context4.fs.ensureDir(target);
|
|
110426
111046
|
await context4.fs.writeFile(globalConfigPath, await context4.fs.readFile(examplePath));
|
|
@@ -110435,10 +111055,10 @@ function registerInitCommand(program2, context4, options = {}) {
|
|
|
110435
111055
|
const minimal = options2.minimal === true;
|
|
110436
111056
|
const adoptGlobal = options2.adoptGlobalConfig === true;
|
|
110437
111057
|
const projectName = options2.name ?? "default";
|
|
110438
|
-
const configPath =
|
|
111058
|
+
const configPath = join37(context4.cwd, CLI_CONFIG.configFile);
|
|
110439
111059
|
const result = { created: [], skipped: [] };
|
|
110440
111060
|
const globalConfigDir = resolveGlobalConfigDir(context4);
|
|
110441
|
-
const globalConfigPath =
|
|
111061
|
+
const globalConfigPath = join37(globalConfigDir, GLOBAL_CONFIG_FILE2);
|
|
110442
111062
|
const globalConfigExists = await context4.fs.exists(globalConfigPath);
|
|
110443
111063
|
let misplacedKeys = [];
|
|
110444
111064
|
if (globalConfigExists) {
|
|
@@ -110451,20 +111071,20 @@ function registerInitCommand(program2, context4, options = {}) {
|
|
|
110451
111071
|
const configRoot = bundledConfigRoot();
|
|
110452
111072
|
if (configRoot !== null && !minimal) {
|
|
110453
111073
|
for (const relPath of listBundledProjectSeedFiles()) {
|
|
110454
|
-
const sourcePath =
|
|
111074
|
+
const sourcePath = join37(configRoot, relPath);
|
|
110455
111075
|
if (!await context4.fs.exists(sourcePath))
|
|
110456
111076
|
continue;
|
|
110457
|
-
const targetPath =
|
|
110458
|
-
await context4.fs.ensureDir(
|
|
111077
|
+
const targetPath = join37(context4.cwd, CLI_CONFIG.configDir, relPath);
|
|
111078
|
+
await context4.fs.ensureDir(join37(targetPath, ".."));
|
|
110459
111079
|
await writeIfNew(context4, targetPath, await context4.fs.readFile(sourcePath), false, result);
|
|
110460
111080
|
}
|
|
110461
111081
|
for (const entry of SCAFFOLD_MANIFEST) {
|
|
110462
|
-
const sourcePath =
|
|
111082
|
+
const sourcePath = join37(configRoot, entry.source);
|
|
110463
111083
|
if (!await context4.fs.exists(sourcePath))
|
|
110464
111084
|
continue;
|
|
110465
|
-
const baseDir = entry.root === true ? context4.cwd :
|
|
110466
|
-
const targetPath =
|
|
110467
|
-
await context4.fs.ensureDir(
|
|
111085
|
+
const baseDir = entry.root === true ? context4.cwd : join37(context4.cwd, CLI_CONFIG.configDir);
|
|
111086
|
+
const targetPath = join37(baseDir, entry.target);
|
|
111087
|
+
await context4.fs.ensureDir(join37(targetPath, ".."));
|
|
110468
111088
|
const entryForce = false;
|
|
110469
111089
|
let body = await context4.fs.readFile(sourcePath);
|
|
110470
111090
|
if (entry.target === "AGENTS.md") {
|
|
@@ -110481,7 +111101,7 @@ function registerInitCommand(program2, context4, options = {}) {
|
|
|
110481
111101
|
const backupPath = `${globalConfigPath}.bak-${new Date().toISOString().replace(/[:.]/g, "-")}`;
|
|
110482
111102
|
await context4.fs.writeFile(backupPath, await context4.fs.readFile(globalConfigPath));
|
|
110483
111103
|
const bundledRoot = bundledConfigRoot();
|
|
110484
|
-
const examplePath = bundledRoot !== null ?
|
|
111104
|
+
const examplePath = bundledRoot !== null ? join37(bundledRoot, GLOBAL_CONFIG_EXAMPLE) : null;
|
|
110485
111105
|
if (examplePath !== null && await context4.fs.exists(examplePath)) {
|
|
110486
111106
|
await context4.fs.writeFile(globalConfigPath, await context4.fs.readFile(examplePath));
|
|
110487
111107
|
adoptedGlobal = true;
|
|
@@ -110551,13 +111171,13 @@ function registerInitCommand(program2, context4, options = {}) {
|
|
|
110551
111171
|
].join(`
|
|
110552
111172
|
`)}
|
|
110553
111173
|
`;
|
|
110554
|
-
await context4.fs.ensureDir(
|
|
111174
|
+
await context4.fs.ensureDir(join37(context4.cwd, CLI_CONFIG.configDir));
|
|
110555
111175
|
await context4.fs.writeFile(configPath, configYaml);
|
|
110556
111176
|
result.created.push(configPath);
|
|
110557
|
-
const agentsDir =
|
|
111177
|
+
const agentsDir = join37(context4.cwd, CLI_CONFIG.configDir, "agents");
|
|
110558
111178
|
await context4.fs.ensureDir(agentsDir);
|
|
110559
|
-
await writeIfNew(context4,
|
|
110560
|
-
const gitignorePath =
|
|
111179
|
+
await writeIfNew(context4, join37(agentsDir, ".gitkeep"), "", force, result);
|
|
111180
|
+
const gitignorePath = join37(context4.cwd, ".gitignore");
|
|
110561
111181
|
const contextEntry = ".spur/context/";
|
|
110562
111182
|
if (await context4.fs.exists(gitignorePath)) {
|
|
110563
111183
|
const existing = await context4.fs.readFile(gitignorePath);
|
|
@@ -110577,20 +111197,20 @@ ${contextEntry}
|
|
|
110577
111197
|
const configRoot = bundledConfigRoot();
|
|
110578
111198
|
if (configRoot !== null) {
|
|
110579
111199
|
for (const relPath of listBundledProjectSeedFiles()) {
|
|
110580
|
-
const sourcePath =
|
|
111200
|
+
const sourcePath = join37(configRoot, relPath);
|
|
110581
111201
|
if (!await context4.fs.exists(sourcePath))
|
|
110582
111202
|
continue;
|
|
110583
|
-
const targetPath =
|
|
110584
|
-
await context4.fs.ensureDir(
|
|
111203
|
+
const targetPath = join37(context4.cwd, CLI_CONFIG.configDir, relPath);
|
|
111204
|
+
await context4.fs.ensureDir(join37(targetPath, ".."));
|
|
110585
111205
|
await writeIfNew(context4, targetPath, await context4.fs.readFile(sourcePath), force, result);
|
|
110586
111206
|
}
|
|
110587
111207
|
for (const entry of SCAFFOLD_MANIFEST) {
|
|
110588
|
-
const sourcePath =
|
|
111208
|
+
const sourcePath = join37(configRoot, entry.source);
|
|
110589
111209
|
if (!await context4.fs.exists(sourcePath))
|
|
110590
111210
|
continue;
|
|
110591
|
-
const baseDir = entry.root === true ? context4.cwd :
|
|
110592
|
-
const targetPath =
|
|
110593
|
-
await context4.fs.ensureDir(
|
|
111211
|
+
const baseDir = entry.root === true ? context4.cwd : join37(context4.cwd, CLI_CONFIG.configDir);
|
|
111212
|
+
const targetPath = join37(baseDir, entry.target);
|
|
111213
|
+
await context4.fs.ensureDir(join37(targetPath, ".."));
|
|
110594
111214
|
const entryForce = entry.preserve === true ? false : force;
|
|
110595
111215
|
let body = await context4.fs.readFile(sourcePath);
|
|
110596
111216
|
if (entry.target === "AGENTS.md") {
|
|
@@ -110603,7 +111223,7 @@ ${contextEntry}
|
|
|
110603
111223
|
}
|
|
110604
111224
|
}
|
|
110605
111225
|
}
|
|
110606
|
-
const agentsMdPath =
|
|
111226
|
+
const agentsMdPath = join37(context4.cwd, "AGENTS.md");
|
|
110607
111227
|
if (await context4.fs.exists(agentsMdPath)) {
|
|
110608
111228
|
const existing = await context4.fs.readFile(agentsMdPath);
|
|
110609
111229
|
if (!existing.includes(INDEXED_CONTEXT_MARKER)) {
|
|
@@ -110644,11 +111264,11 @@ ${contextEntry}
|
|
|
110644
111264
|
// src/commands/maintain.ts
|
|
110645
111265
|
init_src();
|
|
110646
111266
|
init_src2();
|
|
110647
|
-
import { join as
|
|
111267
|
+
import { join as join38 } from "path";
|
|
110648
111268
|
function registerMaintainCommand(program2, context4, options = {}) {
|
|
110649
111269
|
program2.command("maintain", { hidden: options.hidden === true }).summary("run database maintenance (optimize, WAL checkpoint truncation, optional VACUUM)").description("Run database maintenance: PRAGMA optimize, WAL checkpoint truncation; optionally VACUUM compaction.").option("--vacuum", "Run VACUUM defragmentation and page compaction", false).option(...SHARED_OPTIONS.jsonSupported).option(...SHARED_OPTIONS.jsonEnvelope).action(async (options2) => {
|
|
110650
111270
|
const db2 = await context4.getDb();
|
|
110651
|
-
const dbPath = context4.env.DATABASE_URL ??
|
|
111271
|
+
const dbPath = context4.env.DATABASE_URL ?? join38(context4.cwd, DEFAULT_DATABASE_URL);
|
|
110652
111272
|
const result = await maintainDatabase(db2, {
|
|
110653
111273
|
vacuum: options2.vacuum === true,
|
|
110654
111274
|
dbPath: dbPath.startsWith(":memory:") ? undefined : dbPath
|
|
@@ -110971,15 +111591,15 @@ async function runMessageWatch(svc, output2, options, runtime = {}) {
|
|
|
110971
111591
|
}
|
|
110972
111592
|
}
|
|
110973
111593
|
function defaultSleep(ms, signal) {
|
|
110974
|
-
return new Promise((
|
|
111594
|
+
return new Promise((resolve19) => {
|
|
110975
111595
|
if (signal?.aborted) {
|
|
110976
|
-
|
|
111596
|
+
resolve19();
|
|
110977
111597
|
return;
|
|
110978
111598
|
}
|
|
110979
|
-
const timer = setTimeout(
|
|
111599
|
+
const timer = setTimeout(resolve19, ms);
|
|
110980
111600
|
signal?.addEventListener("abort", () => {
|
|
110981
111601
|
clearTimeout(timer);
|
|
110982
|
-
|
|
111602
|
+
resolve19();
|
|
110983
111603
|
}, { once: true });
|
|
110984
111604
|
});
|
|
110985
111605
|
}
|
|
@@ -111006,15 +111626,15 @@ function parseTimeout2(raw) {
|
|
|
111006
111626
|
return n3;
|
|
111007
111627
|
}
|
|
111008
111628
|
function sendSleep(ms, signal) {
|
|
111009
|
-
return new Promise((
|
|
111629
|
+
return new Promise((resolve19) => {
|
|
111010
111630
|
if (signal?.aborted) {
|
|
111011
|
-
|
|
111631
|
+
resolve19();
|
|
111012
111632
|
return;
|
|
111013
111633
|
}
|
|
111014
|
-
const timer = setTimeout(
|
|
111634
|
+
const timer = setTimeout(resolve19, ms);
|
|
111015
111635
|
signal?.addEventListener("abort", () => {
|
|
111016
111636
|
clearTimeout(timer);
|
|
111017
|
-
|
|
111637
|
+
resolve19();
|
|
111018
111638
|
}, { once: true });
|
|
111019
111639
|
});
|
|
111020
111640
|
}
|
|
@@ -111072,10 +111692,10 @@ async function waitForPendingDrain(agentService, teamService, pin, timeoutMs, si
|
|
|
111072
111692
|
|
|
111073
111693
|
// src/commands/migrate.ts
|
|
111074
111694
|
init_src2();
|
|
111075
|
-
import { join as
|
|
111695
|
+
import { join as join39 } from "path";
|
|
111076
111696
|
function registerMigrateCommand(program2, context4, options = {}) {
|
|
111077
111697
|
program2.command("migrate", { hidden: options.hidden === true }).summary("apply CLI-owned schema migrations").option(...SHARED_OPTIONS.json).option(...SHARED_OPTIONS.jsonEnvelope).action(async (options2) => {
|
|
111078
|
-
const migrations = await loadSqlMigrations(
|
|
111698
|
+
const migrations = await loadSqlMigrations(join39(context4.cwd, "drizzle")).catch(() => {
|
|
111079
111699
|
return;
|
|
111080
111700
|
});
|
|
111081
111701
|
const applied = await applyCliMigrations(await context4.getDb(), migrations);
|
|
@@ -111088,16 +111708,16 @@ function registerMigrateCommand(program2, context4, options = {}) {
|
|
|
111088
111708
|
init_src2();
|
|
111089
111709
|
init_dist5();
|
|
111090
111710
|
await init_src3();
|
|
111091
|
-
import { basename as
|
|
111711
|
+
import { basename as basename13, dirname as dirname25, join as join40, resolve as resolve19 } from "path";
|
|
111092
111712
|
function registerProjectsCommand(program2, context4) {
|
|
111093
111713
|
const projectsCmd = program2.command("projects").summary("manage the Spur multi-project registry");
|
|
111094
111714
|
projectsCmd.command("add").argument("<path>", "Project root directory path").option(...SHARED_OPTIONS.nameProjectDisplay).option(...SHARED_OPTIONS.jsonProjectsResponse).option(...SHARED_OPTIONS.jsonEnvelope).action(async (pathArg, options) => {
|
|
111095
111715
|
try {
|
|
111096
|
-
const absolutePath =
|
|
111716
|
+
const absolutePath = resolve19(context4.cwd, pathArg);
|
|
111097
111717
|
if (!await context4.fs.exists(absolutePath)) {
|
|
111098
111718
|
throw new Error(`Directory does not exist: ${absolutePath}`);
|
|
111099
111719
|
}
|
|
111100
|
-
const name = options.name ??
|
|
111720
|
+
const name = options.name ?? basename13(absolutePath);
|
|
111101
111721
|
const registry3 = new ProjectRegistry;
|
|
111102
111722
|
const entry = await registry3.upsert({ path: absolutePath, name, port: 0 });
|
|
111103
111723
|
if (options.json) {
|
|
@@ -111146,8 +111766,8 @@ function registerProjectsCommand(program2, context4) {
|
|
|
111146
111766
|
running: p.port > 0 ? await isPortLive(p.port) : false
|
|
111147
111767
|
})));
|
|
111148
111768
|
const openProjectDb = async (projectPath) => {
|
|
111149
|
-
const url2 =
|
|
111150
|
-
await context4.fs.ensureDir(
|
|
111769
|
+
const url2 = join40(projectPath, ".spur", "spur.db");
|
|
111770
|
+
await context4.fs.ensureDir(dirname25(url2));
|
|
111151
111771
|
return createMigratedDb({ url: url2 });
|
|
111152
111772
|
};
|
|
111153
111773
|
const fleets = options.fleet ? await Promise.all(projects3.map(async (p) => {
|
|
@@ -111589,7 +112209,7 @@ function formatTraceDetail(detail) {
|
|
|
111589
112209
|
// src/commands/serve.ts
|
|
111590
112210
|
init_src();
|
|
111591
112211
|
import { statSync as statSync7 } from "fs";
|
|
111592
|
-
import { join as
|
|
112212
|
+
import { join as join44, resolve as resolve20 } from "path";
|
|
111593
112213
|
|
|
111594
112214
|
// ../server/src/index.ts
|
|
111595
112215
|
init_src();
|
|
@@ -111599,11 +112219,11 @@ init_src();
|
|
|
111599
112219
|
init_loader();
|
|
111600
112220
|
init_src2();
|
|
111601
112221
|
await init_src3();
|
|
111602
|
-
import { basename as
|
|
112222
|
+
import { basename as basename15, dirname as dirname27, isAbsolute as isAbsolute8, join as join43 } from "path";
|
|
111603
112223
|
init_dist5();
|
|
111604
112224
|
|
|
111605
112225
|
// ../server/src/bootstrap.ts
|
|
111606
|
-
import { join as
|
|
112226
|
+
import { join as join41 } from "path";
|
|
111607
112227
|
|
|
111608
112228
|
// ../../node_modules/.bun/radash@12.1.1/node_modules/radash/dist/esm/async.mjs
|
|
111609
112229
|
var guard = (func, shouldGuard) => {
|
|
@@ -111876,8 +112496,8 @@ class EventPublisher {
|
|
|
111876
112496
|
if (bufferedEvents.length > 0) {
|
|
111877
112497
|
return { done: false, value: bufferedEvents.shift() };
|
|
111878
112498
|
}
|
|
111879
|
-
return new Promise((
|
|
111880
|
-
pullResolvers.push([
|
|
112499
|
+
return new Promise((resolve20, reject) => {
|
|
112500
|
+
pullResolvers.push([resolve20, reject]);
|
|
111881
112501
|
});
|
|
111882
112502
|
}, async () => {
|
|
111883
112503
|
unsubscribe();
|
|
@@ -116997,6 +117617,55 @@ function mountMiddleware(app, appRt) {
|
|
|
116997
117617
|
}
|
|
116998
117618
|
}
|
|
116999
117619
|
|
|
117620
|
+
// ../server/src/modules/commands/index.ts
|
|
117621
|
+
init_src();
|
|
117622
|
+
await init_src3();
|
|
117623
|
+
var commandsModule = {
|
|
117624
|
+
name: "commands",
|
|
117625
|
+
mount(app, ctx) {
|
|
117626
|
+
const projectRoot = ctx?.cwd ?? process.cwd();
|
|
117627
|
+
const filePath = getSlashCommandsFilePath();
|
|
117628
|
+
const service = new SlashCommandsService({ filePath, projectRoot });
|
|
117629
|
+
let cachedCommands = [];
|
|
117630
|
+
try {
|
|
117631
|
+
cachedCommands = service.getCommands();
|
|
117632
|
+
} catch {}
|
|
117633
|
+
const handleList = (c3) => {
|
|
117634
|
+
const reload = c3.req.query("reload") === "true";
|
|
117635
|
+
if (reload || cachedCommands.length === 0) {
|
|
117636
|
+
try {
|
|
117637
|
+
cachedCommands = service.getCommands(reload);
|
|
117638
|
+
} catch {}
|
|
117639
|
+
}
|
|
117640
|
+
return c3.json({
|
|
117641
|
+
commands: cachedCommands,
|
|
117642
|
+
count: cachedCommands.length,
|
|
117643
|
+
filePath: service.getFilePath()
|
|
117644
|
+
});
|
|
117645
|
+
};
|
|
117646
|
+
app.get("/api/commands", handleList);
|
|
117647
|
+
app.get("/api/agent/commands", handleList);
|
|
117648
|
+
app.post("/api/commands/generate", (c3) => {
|
|
117649
|
+
try {
|
|
117650
|
+
const result = service.generate();
|
|
117651
|
+
cachedCommands = result.commands;
|
|
117652
|
+
return c3.json({
|
|
117653
|
+
ok: true,
|
|
117654
|
+
count: result.count,
|
|
117655
|
+
filePath: result.path,
|
|
117656
|
+
commands: result.commands
|
|
117657
|
+
});
|
|
117658
|
+
} catch (err) {
|
|
117659
|
+
return c3.json({
|
|
117660
|
+
ok: false,
|
|
117661
|
+
error: err instanceof Error ? err.message : String(err),
|
|
117662
|
+
filePath: service.getFilePath()
|
|
117663
|
+
}, 500);
|
|
117664
|
+
}
|
|
117665
|
+
});
|
|
117666
|
+
}
|
|
117667
|
+
};
|
|
117668
|
+
|
|
117000
117669
|
// ../server/src/modules/sse/stream-helpers.ts
|
|
117001
117670
|
function sendSseKeepalive(closed, controller, encoder) {
|
|
117002
117671
|
if (closed.current)
|
|
@@ -118305,7 +118974,7 @@ var featureModule = {
|
|
|
118305
118974
|
// ../server/src/modules/health/index.ts
|
|
118306
118975
|
init_src2();
|
|
118307
118976
|
await init_src3();
|
|
118308
|
-
import { basename as
|
|
118977
|
+
import { basename as basename14 } from "path";
|
|
118309
118978
|
var startedAt = Date.now();
|
|
118310
118979
|
var OPERATOR_AGENT_ID = "board-operator";
|
|
118311
118980
|
var healthModule = {
|
|
@@ -118332,7 +119001,7 @@ var healthModule = {
|
|
|
118332
119001
|
return c3.json({ status: "error", db: "unreachable" }, 503);
|
|
118333
119002
|
});
|
|
118334
119003
|
app.get("/api/project", (c3) => {
|
|
118335
|
-
return c3.json({ name: ctx ?
|
|
119004
|
+
return c3.json({ name: ctx ? basename14(ctx.cwd) : null, path: ctx ? normalizeProjectPath(ctx.cwd) : null });
|
|
118336
119005
|
});
|
|
118337
119006
|
app.get("/api/project/fleet", async (c3) => {
|
|
118338
119007
|
if (!ctx) {
|
|
@@ -119412,7 +120081,8 @@ var builtins = [
|
|
|
119412
120081
|
processesModule,
|
|
119413
120082
|
observabilityModule,
|
|
119414
120083
|
runsModule,
|
|
119415
|
-
historyModule
|
|
120084
|
+
historyModule,
|
|
120085
|
+
commandsModule
|
|
119416
120086
|
];
|
|
119417
120087
|
function registerModules(app, ctx, modules = builtins) {
|
|
119418
120088
|
for (const mod of modules) {
|
|
@@ -121430,7 +122100,7 @@ function createApp(appRt, opts) {
|
|
|
121430
122100
|
app.use("*", async (c3, next) => {
|
|
121431
122101
|
const pathname = c3.req.path === "/" ? "/index.html" : c3.req.path;
|
|
121432
122102
|
try {
|
|
121433
|
-
const file2 = Bun.file(
|
|
122103
|
+
const file2 = Bun.file(join41(webDistPath, pathname));
|
|
121434
122104
|
if (await file2.exists()) {
|
|
121435
122105
|
const headers = new Headers({ "content-type": file2.type });
|
|
121436
122106
|
return new Response(file2.stream(), { headers });
|
|
@@ -121443,7 +122113,7 @@ function createApp(appRt, opts) {
|
|
|
121443
122113
|
return c3.json({ error: "Not Found" }, 404);
|
|
121444
122114
|
}
|
|
121445
122115
|
try {
|
|
121446
|
-
const indexFile = Bun.file(
|
|
122116
|
+
const indexFile = Bun.file(join41(webDistPath, "index.html"));
|
|
121447
122117
|
if (await indexFile.exists()) {
|
|
121448
122118
|
return new Response(indexFile.stream(), {
|
|
121449
122119
|
headers: { "content-type": "text/html; charset=utf-8" }
|
|
@@ -121465,7 +122135,7 @@ init_loader();
|
|
|
121465
122135
|
init_src2();
|
|
121466
122136
|
init_dist5();
|
|
121467
122137
|
await init_src3();
|
|
121468
|
-
import { dirname as
|
|
122138
|
+
import { dirname as dirname26, join as join42 } from "path";
|
|
121469
122139
|
var NOOP_OUTPUT = { write: (_s) => {}, error: (_s) => {} };
|
|
121470
122140
|
|
|
121471
122141
|
class LazyPlanningEventEmitter {
|
|
@@ -121501,7 +122171,7 @@ class NotConfiguredError extends Error {
|
|
|
121501
122171
|
function createServerContext(appRt, options) {
|
|
121502
122172
|
const cwd = options.cwd;
|
|
121503
122173
|
const fs3 = options.fs;
|
|
121504
|
-
const dbUrl = options.dbUrl ??
|
|
122174
|
+
const dbUrl = options.dbUrl ?? join42(cwd, DEFAULT_DATABASE_URL);
|
|
121505
122175
|
const eventsBus = options.eventsBus ?? appRt.events;
|
|
121506
122176
|
const jobQueueEnabled = options.jobQueueEnabled ?? false;
|
|
121507
122177
|
const eventProjectContext = systemEventProjectContext(cwd);
|
|
@@ -121538,7 +122208,7 @@ function createServerContext(appRt, options) {
|
|
|
121538
122208
|
if (!dbPromise) {
|
|
121539
122209
|
const created = (async () => {
|
|
121540
122210
|
if (dbUrl !== IN_MEMORY_DATABASE_URL) {
|
|
121541
|
-
await fs3.ensureDir(
|
|
122211
|
+
await fs3.ensureDir(dirname26(dbUrl));
|
|
121542
122212
|
}
|
|
121543
122213
|
return createMigratedDbViaRuntime({ url: dbUrl });
|
|
121544
122214
|
})();
|
|
@@ -121806,7 +122476,7 @@ async function loadServerSectionMatrix(projectRoot) {
|
|
|
121806
122476
|
}
|
|
121807
122477
|
const root = bundledConfigRoot();
|
|
121808
122478
|
if (root !== null) {
|
|
121809
|
-
const matrixPath =
|
|
122479
|
+
const matrixPath = join43(root, "tasks", "section-matrix.yaml");
|
|
121810
122480
|
if (await nodeFs.exists(matrixPath)) {
|
|
121811
122481
|
return await loadStructuredSpurConfig(matrixPath, {
|
|
121812
122482
|
validateJsonSchema: false
|
|
@@ -121815,7 +122485,7 @@ async function loadServerSectionMatrix(projectRoot) {
|
|
|
121815
122485
|
}
|
|
121816
122486
|
throw new Error(`no canonical section-matrix found for task creation (F92 R1); tried:
|
|
121817
122487
|
` + ` - ${localPath}
|
|
121818
|
-
` + (root !== null ? ` - ${
|
|
122488
|
+
` + (root !== null ? ` - ${join43(root, "tasks", "section-matrix.yaml")}
|
|
121819
122489
|
` : "") + "copy/generate section-matrix.yaml from the canonical build-time matrix asset (repo `config` `tasks` tree) into one of those paths");
|
|
121820
122490
|
}
|
|
121821
122491
|
var SYSTEM_EVENTS_PRUNE_JOB = "system-events-prune";
|
|
@@ -121880,7 +122550,7 @@ function registerSchedulerEntries(scheduler3, ctx, jobs = [], options = {}) {
|
|
|
121880
122550
|
});
|
|
121881
122551
|
for (const job of jobs) {
|
|
121882
122552
|
const schedule = job.cron ?? String(job.intervalMinutes * 60000);
|
|
121883
|
-
const jobPolicy = resolveSchedulerJobTimeoutMs(job.name, env, timeoutMs);
|
|
122553
|
+
const jobPolicy = resolveSchedulerJobTimeoutMs(job.name, env, timeoutMs, job.timeoutMs);
|
|
121884
122554
|
const sweepThresholdMs = jobPolicy === null ? null : jobPolicy + resolveKillGraceMs3(env);
|
|
121885
122555
|
register(schedule, `${SCHEDULER_CUSTOM_JOB}:${job.name}`, async () => {
|
|
121886
122556
|
const queue = await ctx.jobQueue();
|
|
@@ -122050,15 +122720,15 @@ async function handleFeatureActionJob(ctx, env, payload) {
|
|
|
122050
122720
|
await runFeatureActionJob(ctx, env, payload);
|
|
122051
122721
|
}
|
|
122052
122722
|
async function resolveWebDistPath(configuredPath, projectRoot = process.cwd()) {
|
|
122053
|
-
const candidates = configuredPath && configuredPath.trim() !== "" ? [isAbsolute8(configuredPath) ? configuredPath :
|
|
122054
|
-
|
|
122055
|
-
|
|
122056
|
-
|
|
122057
|
-
|
|
122058
|
-
|
|
122723
|
+
const candidates = configuredPath && configuredPath.trim() !== "" ? [isAbsolute8(configuredPath) ? configuredPath : join43(projectRoot, configuredPath)] : [
|
|
122724
|
+
join43(projectRoot, "dist/web"),
|
|
122725
|
+
join43(import.meta.dir, "web"),
|
|
122726
|
+
join43(dirname27(process.execPath), "web"),
|
|
122727
|
+
join43(dirname27(process.execPath), "../web"),
|
|
122728
|
+
join43(import.meta.dir, "../../../dist/web")
|
|
122059
122729
|
];
|
|
122060
122730
|
for (const candidate of candidates) {
|
|
122061
|
-
if (await Bun.file(
|
|
122731
|
+
if (await Bun.file(join43(candidate, "index.html")).exists()) {
|
|
122062
122732
|
return candidate;
|
|
122063
122733
|
}
|
|
122064
122734
|
}
|
|
@@ -122075,7 +122745,7 @@ async function startServer(options, deps = defaultDeps) {
|
|
|
122075
122745
|
async start(appRt) {
|
|
122076
122746
|
const fs3 = deps.createNodeFileSystem(projectRoot);
|
|
122077
122747
|
if (options.dbUrl && options.dbUrl !== IN_MEMORY_DATABASE_URL) {
|
|
122078
|
-
await fs3.ensureDir(
|
|
122748
|
+
await fs3.ensureDir(dirname27(options.dbUrl));
|
|
122079
122749
|
}
|
|
122080
122750
|
const scheduler3 = appRt.config.scheduler.enabled ? appRt.scheduler : undefined;
|
|
122081
122751
|
const webDistPath = await resolveWebDistPath(options.webDistPath, projectRoot);
|
|
@@ -122211,10 +122881,11 @@ async function startServer(options, deps = defaultDeps) {
|
|
|
122211
122881
|
});
|
|
122212
122882
|
registry3.register(SCHEDULER_CUSTOM_JOB, async (job) => {
|
|
122213
122883
|
await emitQueueJobStarted(ctx, job);
|
|
122884
|
+
const declaredTimeouts = new Map(appRt.config.scheduler.jobs.map((entry) => [entry.name, entry.timeoutMs]));
|
|
122214
122885
|
return handleSchedulerCustomJob({
|
|
122215
122886
|
cwd: ctx.cwd,
|
|
122216
122887
|
executor: childExecutor,
|
|
122217
|
-
resolveTimeoutMs: (name) => resolveSchedulerJobTimeoutMs(name, env, schedulerCustomTimeoutMs),
|
|
122888
|
+
resolveTimeoutMs: (name) => resolveSchedulerJobTimeoutMs(name, env, schedulerCustomTimeoutMs, declaredTimeouts.get(name)),
|
|
122218
122889
|
onDuplicate: (name) => {
|
|
122219
122890
|
ctx.eventBus().emit("scheduler.job.executed", {
|
|
122220
122891
|
name: `${SCHEDULER_CUSTOM_JOB}:${name}`,
|
|
@@ -122223,10 +122894,20 @@ async function startServer(options, deps = defaultDeps) {
|
|
|
122223
122894
|
skipped: true,
|
|
122224
122895
|
reason: `duplicate execution suppressed; ${name} is already running in this process`
|
|
122225
122896
|
});
|
|
122226
|
-
}
|
|
122897
|
+
},
|
|
122898
|
+
onShutdownAbandon: (name) => {
|
|
122899
|
+
ctx.eventBus().emit("scheduler.job.executed", {
|
|
122900
|
+
name: `${SCHEDULER_CUSTOM_JOB}:${name}`,
|
|
122901
|
+
durationMs: 0,
|
|
122902
|
+
severity: "info",
|
|
122903
|
+
skipped: true,
|
|
122904
|
+
reason: `abandoned by server shutdown; the next tick resumes ${name}`
|
|
122905
|
+
});
|
|
122906
|
+
},
|
|
122907
|
+
isShuttingDown: () => shuttingDown
|
|
122227
122908
|
}, job);
|
|
122228
122909
|
});
|
|
122229
|
-
const unlimitedJobNames = appRt.config.scheduler.jobs.filter((job) => resolveSchedulerJobTimeoutMs(job.name, env, schedulerCustomTimeoutMs) === null).map((job) => job.name);
|
|
122910
|
+
const unlimitedJobNames = appRt.config.scheduler.jobs.filter((job) => resolveSchedulerJobTimeoutMs(job.name, env, schedulerCustomTimeoutMs, job.timeoutMs) === null).map((job) => job.name);
|
|
122230
122911
|
const orphanCount = await failOrphanedProcessingJobs(await ctx.getDb(), Date.now(), unlimitedJobNames);
|
|
122231
122912
|
if (orphanCount > 0) {
|
|
122232
122913
|
appRt.logger.warn("Swept orphaned processing jobs at startup", {
|
|
@@ -122254,7 +122935,7 @@ async function startServer(options, deps = defaultDeps) {
|
|
|
122254
122935
|
});
|
|
122255
122936
|
const projectRegistry = new ProjectRegistry;
|
|
122256
122937
|
const projectCwd = projectRoot;
|
|
122257
|
-
const projectName =
|
|
122938
|
+
const projectName = basename15(projectCwd);
|
|
122258
122939
|
try {
|
|
122259
122940
|
const existingProject = await projectRegistry.getByPath(projectCwd);
|
|
122260
122941
|
await projectRegistry.upsert({
|
|
@@ -122374,10 +123055,10 @@ var SQLITE_BUSY_MESSAGE_CONSTANTS = Object.freeze({
|
|
|
122374
123055
|
|
|
122375
123056
|
// src/commands/serve.ts
|
|
122376
123057
|
function resolveServeDbUrl(cwd, env, configuredUrl) {
|
|
122377
|
-
return env.DATABASE_URL === undefined ?
|
|
123058
|
+
return env.DATABASE_URL === undefined ? join44(cwd, DEFAULT_DATABASE_URL) : configuredUrl;
|
|
122378
123059
|
}
|
|
122379
123060
|
function resolveServeCwd(cwd) {
|
|
122380
|
-
const resolved =
|
|
123061
|
+
const resolved = resolve20(cwd);
|
|
122381
123062
|
let isDirectory = false;
|
|
122382
123063
|
try {
|
|
122383
123064
|
isDirectory = statSync7(resolved).isDirectory();
|
|
@@ -122430,7 +123111,7 @@ function registerServeCommand(program2, context4, options = {}) {
|
|
|
122430
123111
|
}
|
|
122431
123112
|
|
|
122432
123113
|
// src/commands/status.ts
|
|
122433
|
-
import { join as
|
|
123114
|
+
import { join as join45 } from "path";
|
|
122434
123115
|
|
|
122435
123116
|
// src/git-context.ts
|
|
122436
123117
|
init_dist5();
|
|
@@ -122478,8 +123159,8 @@ function registerStatusCommand(program2, context4, options = {}) {
|
|
|
122478
123159
|
}
|
|
122479
123160
|
async function runStatusCore(path9, options, context4) {
|
|
122480
123161
|
const [packageJsonExists, spurConfigExists, git3, agentSpecs] = await Promise.all([
|
|
122481
|
-
context4.fs.exists(
|
|
122482
|
-
context4.fs.exists(
|
|
123162
|
+
context4.fs.exists(join45(context4.cwd, "package.json")),
|
|
123163
|
+
context4.fs.exists(join45(context4.cwd, ".spur", "config.yaml")),
|
|
122483
123164
|
gitContext(context4.cwd),
|
|
122484
123165
|
listAgentSpecIds(context4)
|
|
122485
123166
|
]);
|
|
@@ -122507,14 +123188,14 @@ async function runStatusCore(path9, options, context4) {
|
|
|
122507
123188
|
return status.ok ? 0 : 1;
|
|
122508
123189
|
}
|
|
122509
123190
|
async function listAgentSpecIds(context4) {
|
|
122510
|
-
const dir =
|
|
123191
|
+
const dir = join45(context4.cwd, ".spur", "agents");
|
|
122511
123192
|
if (!await context4.fs.exists(dir))
|
|
122512
123193
|
return [];
|
|
122513
123194
|
const entries = await context4.fs.readDir(dir);
|
|
122514
123195
|
return entries.filter((entry) => entry.endsWith(".yaml") || entry.endsWith(".yml")).map((entry) => entry.replace(/\.ya?ml$/, "")).sort();
|
|
122515
123196
|
}
|
|
122516
123197
|
async function readTargetStatus(context4, targetPath) {
|
|
122517
|
-
const resolved =
|
|
123198
|
+
const resolved = join45(context4.cwd, targetPath);
|
|
122518
123199
|
const stat2 = await context4.fs.stat(resolved);
|
|
122519
123200
|
if (stat2 === null)
|
|
122520
123201
|
throw new CommandError(`status failed: path does not exist at ${resolved}`);
|
|
@@ -122527,7 +123208,7 @@ init_dist7();
|
|
|
122527
123208
|
init_dist5();
|
|
122528
123209
|
await init_src3();
|
|
122529
123210
|
import { closeSync as closeSync5, fstatSync as fstatSync3, openSync as openSync5, readSync as readSync3 } from "fs";
|
|
122530
|
-
import { join as
|
|
123211
|
+
import { join as join46 } from "path";
|
|
122531
123212
|
import { createInterface } from "readline";
|
|
122532
123213
|
import { setTimeout as sleep5 } from "timers/promises";
|
|
122533
123214
|
|
|
@@ -122706,9 +123387,9 @@ async function writeWorkflowPlanArtifact(cwd, runId, resolved, secretValues) {
|
|
|
122706
123387
|
}))
|
|
122707
123388
|
});
|
|
122708
123389
|
const { mkdir, writeFile: writeFile2 } = await import("fs/promises");
|
|
122709
|
-
const artifactsDir =
|
|
123390
|
+
const artifactsDir = join46(cwd, ".spur", "run");
|
|
122710
123391
|
await mkdir(artifactsDir, { recursive: true });
|
|
122711
|
-
const artifactPath =
|
|
123392
|
+
const artifactPath = join46(artifactsDir, `${runId}-workflow-plan.json`);
|
|
122712
123393
|
await writeFile2(artifactPath, payload);
|
|
122713
123394
|
return artifactPath;
|
|
122714
123395
|
}
|
|
@@ -122993,7 +123674,7 @@ Monitor with: spur workflow trace ${runId2} --follow`);
|
|
|
122993
123674
|
}
|
|
122994
123675
|
const runLog = options.log === false ? undefined : new WorkflowRunLogSink({
|
|
122995
123676
|
bus,
|
|
122996
|
-
dir:
|
|
123677
|
+
dir: join46(context4.cwd, ".spur", "run"),
|
|
122997
123678
|
runId,
|
|
122998
123679
|
...planPreview !== undefined ? { planPreview } : {},
|
|
122999
123680
|
...resolveOutputLogConfig(context4.spurConfig ?? null)
|
|
@@ -123531,7 +124212,7 @@ function readRunLogChunk(logPath, offset) {
|
|
|
123531
124212
|
}
|
|
123532
124213
|
}
|
|
123533
124214
|
async function followRunLog(service, runId, dir, pollMs, write, wait = (ms) => sleep5(ms)) {
|
|
123534
|
-
const logPath =
|
|
124215
|
+
const logPath = join46(dir, ".spur", "run", `${runId}.log`);
|
|
123535
124216
|
let offset = 0;
|
|
123536
124217
|
let everRead = false;
|
|
123537
124218
|
while (true) {
|
|
@@ -123594,7 +124275,7 @@ init_src();
|
|
|
123594
124275
|
init_src2();
|
|
123595
124276
|
init_dist5();
|
|
123596
124277
|
await init_src3();
|
|
123597
|
-
import { dirname as
|
|
124278
|
+
import { dirname as dirname28, join as join47, resolve as resolve21 } from "path";
|
|
123598
124279
|
import { isatty as isatty4 } from "tty";
|
|
123599
124280
|
|
|
123600
124281
|
// ../../node_modules/.bun/@clack+core@1.4.1/node_modules/@clack/core/dist/index.mjs
|
|
@@ -124978,7 +125659,7 @@ class DefaultHitlResponder {
|
|
|
124978
125659
|
// src/context.ts
|
|
124979
125660
|
await init_src3();
|
|
124980
125661
|
function createCliContext(options) {
|
|
124981
|
-
const cwd =
|
|
125662
|
+
const cwd = resolve21(options.cwd ?? process.cwd());
|
|
124982
125663
|
const env = options.env ?? process.env;
|
|
124983
125664
|
const fs3 = createNodeFileSystem3(cwd);
|
|
124984
125665
|
const agentConfig = options.agentConfig ?? options.spurConfig?.agent;
|
|
@@ -125031,10 +125712,10 @@ function createCliContext(options) {
|
|
|
125031
125712
|
function noopSetExitCode(_code) {}
|
|
125032
125713
|
async function createMigratedDbAdapter(cwd = process.cwd(), env = process.env, dbUrl) {
|
|
125033
125714
|
const config4 = buildConfigFromEnv(env);
|
|
125034
|
-
const configuredUrl = env.DATABASE_URL === undefined ?
|
|
125715
|
+
const configuredUrl = env.DATABASE_URL === undefined ? join47(cwd, DEFAULT_DATABASE_URL) : config4.database.url;
|
|
125035
125716
|
const url2 = dbUrl ?? configuredUrl;
|
|
125036
125717
|
if (url2 !== IN_MEMORY_DATABASE_URL) {
|
|
125037
|
-
await createNodeFileSystem3().ensureDir(
|
|
125718
|
+
await createNodeFileSystem3().ensureDir(dirname28(url2));
|
|
125038
125719
|
}
|
|
125039
125720
|
return createMigratedDb({ url: url2 });
|
|
125040
125721
|
}
|