@gobing-ai/spur 0.3.82 → 0.3.84
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/rules/strict/runtime-boundaries.yaml +1 -0
- package/package.json +1 -1
- package/plugins/sp/README.md +17 -11
- package/plugins/sp/lib/idea-handoff.generated.mjs +53 -25
- package/plugins/sp/plugin.json +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/spur-config.schema.json +5 -0
- package/schemas/task-batch.schema.json +5 -0
- package/spur.js +910 -358
- package/web/_astro/{BoardApp.D8bM9pKL.js → BoardApp.D4mBUGLT.js} +64 -64
- package/web/_astro/BoardApp.DFPYpI5O.js +1 -0
- package/web/_astro/{TaskDetail.BPRqgVUE.js → TaskDetail.DhCWaOPR.js} +1 -1
- package/web/_astro/{arc.BPrPES3z.js → arc.D3X0nfq-.js} +1 -1
- package/web/_astro/{architectureDiagram-3BPJPVTR.qX_7q02P.js → architectureDiagram-3BPJPVTR.BQDoiGme.js} +1 -1
- package/web/_astro/{blockDiagram-GPEHLZMM.CUZfj5V7.js → blockDiagram-GPEHLZMM.BjHekBbM.js} +1 -1
- package/web/_astro/{c4Diagram-AAUBKEIU.CTaOr8hH.js → c4Diagram-AAUBKEIU.BvHt171l.js} +1 -1
- package/web/_astro/channel.BlQwxyoI.js +1 -0
- package/web/_astro/{chunk-2J33WTMH.Dt-9wf3h.js → chunk-2J33WTMH.CwxGXlDR.js} +1 -1
- package/web/_astro/{chunk-4BX2VUAB.CTC2sdoN.js → chunk-4BX2VUAB.Dia1YtwU.js} +1 -1
- package/web/_astro/{chunk-55IACEB6.DQcxt2_g.js → chunk-55IACEB6.Oz9epCXa.js} +1 -1
- package/web/_astro/{chunk-727SXJPM.DXFPSn-a.js → chunk-727SXJPM.DKFYJjYJ.js} +1 -1
- package/web/_astro/{chunk-AQP2D5EJ.BCx3U4bT.js → chunk-AQP2D5EJ.AD7w5oVz.js} +1 -1
- package/web/_astro/{chunk-FMBD7UC4.DL2tJkdO.js → chunk-FMBD7UC4.D0OqTj52.js} +1 -1
- package/web/_astro/{chunk-ND2GUHAM.DZyflMro.js → chunk-ND2GUHAM.CU5ppR7j.js} +1 -1
- package/web/_astro/{chunk-QZHKN3VN.CUI2mT09.js → chunk-QZHKN3VN.Bu1rFztw.js} +1 -1
- package/web/_astro/{classDiagram-4FO5ZUOK.g4rX4Fr1.js → classDiagram-4FO5ZUOK.C0Pgm5yE.js} +1 -1
- package/web/_astro/{classDiagram-v2-Q7XG4LA2.g4rX4Fr1.js → classDiagram-v2-Q7XG4LA2.C0Pgm5yE.js} +1 -1
- package/web/_astro/{cose-bilkent-S5V4N54A.CzWJLqp0.js → cose-bilkent-S5V4N54A.C0ecLb1e.js} +1 -1
- package/web/_astro/{cynefin-OW5HDTMX.WgsvQeCR.js → cynefin-OW5HDTMX.CpStVmhO.js} +1 -1
- package/web/_astro/{dagre-BM42HDAG.Dzv6ngql.js → dagre-BM42HDAG.BPWN3W9f.js} +1 -1
- package/web/_astro/{diagram-2AECGRRQ.CpJ4a9rU.js → diagram-2AECGRRQ.DnXbEot4.js} +1 -1
- package/web/_astro/{diagram-5GNKFQAL.CzPlF_dq.js → diagram-5GNKFQAL.BYJsgUiI.js} +1 -1
- package/web/_astro/{diagram-KO2AKTUF.TAkZNTcQ.js → diagram-KO2AKTUF.CU4Bqprx.js} +1 -1
- package/web/_astro/{diagram-LMA3HP47.uCjoKSag.js → diagram-LMA3HP47.BQLOANIu.js} +1 -1
- package/web/_astro/{diagram-OG6HWLK6.eMplIjoK.js → diagram-OG6HWLK6.Cn1UVhqj.js} +1 -1
- package/web/_astro/{erDiagram-TEJ5UH35.Bf7zoXGz.js → erDiagram-TEJ5UH35.C8FdZyvA.js} +1 -1
- package/web/_astro/{flowDiagram-I6XJVG4X.B_bHj3gN.js → flowDiagram-I6XJVG4X.QSZCR8Uo.js} +1 -1
- package/web/_astro/{ganttDiagram-6RSMTGT7.BasrHRMj.js → ganttDiagram-6RSMTGT7.O1Py31MY.js} +1 -1
- package/web/_astro/{gitGraphDiagram-PVQCEYII.C6iphq1x.js → gitGraphDiagram-PVQCEYII.DoeJRBfY.js} +1 -1
- package/web/_astro/index.TPF1FerP.css +1 -0
- package/web/_astro/{infoDiagram-5YYISTIA.HXmDMhW4.js → infoDiagram-5YYISTIA.9npLJecW.js} +1 -1
- package/web/_astro/{ishikawaDiagram-YF4QCWOH.BSmW8NiU.js → ishikawaDiagram-YF4QCWOH.DXanvOr3.js} +1 -1
- package/web/_astro/{journeyDiagram-JHISSGLW.DEQow5fo.js → journeyDiagram-JHISSGLW.DxbVjxAv.js} +1 -1
- package/web/_astro/{kanban-definition-UN3LZRKU.IVm9cTdc.js → kanban-definition-UN3LZRKU.BDk6_iq0.js} +1 -1
- package/web/_astro/{linear.CrsM73_9.js → linear.C9cCDjT3.js} +1 -1
- package/web/_astro/{mermaid.core.CfBeDJls.js → mermaid.core.CpHRe5nH.js} +4 -4
- package/web/_astro/{mindmap-definition-RKZ34NQL.C3j60Y-0.js → mindmap-definition-RKZ34NQL.D-6C85bn.js} +1 -1
- package/web/_astro/{pieDiagram-4H26LBE5.B-aCMeEA.js → pieDiagram-4H26LBE5.B3Oxodty.js} +1 -1
- package/web/_astro/{quadrantDiagram-W4KKPZXB.Cib965yq.js → quadrantDiagram-W4KKPZXB.QZ_9Hshu.js} +1 -1
- package/web/_astro/{requirementDiagram-4Y6WPE33.D61cS4O-.js → requirementDiagram-4Y6WPE33.BimvuK53.js} +1 -1
- package/web/_astro/{sankeyDiagram-5OEKKPKP.GKF2qVPy.js → sankeyDiagram-5OEKKPKP.D5Kkpx2C.js} +1 -1
- package/web/_astro/{sequenceDiagram-3UESZ5HK.DZnq8F2h.js → sequenceDiagram-3UESZ5HK.B4YR6fAE.js} +1 -1
- package/web/_astro/{stateDiagram-AJRCARHV.DXUFmdgM.js → stateDiagram-AJRCARHV.Bt2wkZok.js} +1 -1
- package/web/_astro/{stateDiagram-v2-BHNVJYJU.BtHmhLEz.js → stateDiagram-v2-BHNVJYJU.YZ4X7dfQ.js} +1 -1
- package/web/_astro/{timeline-definition-PNZ67QCA.Cy-WW2ln.js → timeline-definition-PNZ67QCA.DmiTBL8w.js} +1 -1
- package/web/_astro/{vennDiagram-CIIHVFJN.SLp5b9KI.js → vennDiagram-CIIHVFJN.KNJV40mf.js} +1 -1
- package/web/_astro/{wardleyDiagram-YWT4CUSO.Bww45mWV.js → wardleyDiagram-YWT4CUSO.BVAaKgWH.js} +1 -1
- package/web/_astro/{xychartDiagram-2RQKCTM6.DR4swI6a.js → xychartDiagram-2RQKCTM6.D5Jx7zB2.js} +1 -1
- package/web/index.html +2 -2
- package/plugins/README.md +0 -656
- 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,34 @@ 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
|
+
role: exports_external.string().optional(),
|
|
39525
|
+
source: exports_external.string().optional()
|
|
39526
|
+
});
|
|
39527
|
+
slashCommandsFileSchema = exports_external.object({
|
|
39528
|
+
version: exports_external.number().int().default(1),
|
|
39529
|
+
updatedAt: exports_external.string().optional(),
|
|
39530
|
+
commands: exports_external.array(slashCommandItemSchema).default([])
|
|
39531
|
+
});
|
|
39532
|
+
});
|
|
39533
|
+
|
|
39506
39534
|
// ../../packages/config/src/index.ts
|
|
39507
39535
|
function folderPathWithDefault(defaultPath) {
|
|
39508
39536
|
return exports_external.preprocess((v) => v == null ? undefined : v, exports_external.string().default(defaultPath));
|
|
@@ -39608,6 +39636,7 @@ var init_src = __esm(() => {
|
|
|
39608
39636
|
init_zod();
|
|
39609
39637
|
init_finding_codes();
|
|
39610
39638
|
init_projects();
|
|
39639
|
+
init_slash_commands();
|
|
39611
39640
|
SPUR_ENV_VARS = {
|
|
39612
39641
|
nodeEnv: "NODE_ENV",
|
|
39613
39642
|
port: "PORT",
|
|
@@ -39789,6 +39818,7 @@ var init_src = __esm(() => {
|
|
|
39789
39818
|
VersionCarrierEntrySchema = exports_external.looseObject({ type: exports_external.string().min(1) });
|
|
39790
39819
|
BuilderBumpVerConfigSchema = exports_external.object({
|
|
39791
39820
|
tagVersionSeparator: exports_external.string().min(1).default("-v"),
|
|
39821
|
+
aggregatePackage: exports_external.string().min(1).optional(),
|
|
39792
39822
|
publishWorkflow: exports_external.string().min(1).default("publish.yml"),
|
|
39793
39823
|
releaseCommitType: exports_external.string().min(1).default("chore"),
|
|
39794
39824
|
releaseCommitScope: exports_external.string().min(1).default("release"),
|
|
@@ -39834,14 +39864,14 @@ var init_src = __esm(() => {
|
|
|
39834
39864
|
|
|
39835
39865
|
// ../../packages/config/src/bundled-config.ts
|
|
39836
39866
|
import { existsSync as existsSync3, readdirSync as readdirSync3, statSync as statSync4 } from "fs";
|
|
39837
|
-
import { dirname as dirname3, join as
|
|
39867
|
+
import { dirname as dirname3, join as join3 } from "path";
|
|
39838
39868
|
function bundledConfigRoot() {
|
|
39839
39869
|
if (cachedRoot !== undefined)
|
|
39840
39870
|
return cachedRoot;
|
|
39841
39871
|
let dir = import.meta.dirname;
|
|
39842
39872
|
while (true) {
|
|
39843
39873
|
for (const name of BUNDLED_CONFIG_DIRS) {
|
|
39844
|
-
const candidate =
|
|
39874
|
+
const candidate = join3(dir, name);
|
|
39845
39875
|
if (isBundledConfigDir(candidate)) {
|
|
39846
39876
|
cachedRoot = candidate;
|
|
39847
39877
|
return cachedRoot;
|
|
@@ -39856,7 +39886,7 @@ function bundledConfigRoot() {
|
|
|
39856
39886
|
return cachedRoot;
|
|
39857
39887
|
}
|
|
39858
39888
|
function isBundledConfigDir(candidate) {
|
|
39859
|
-
return existsSync3(candidate) && statSync4(candidate).isDirectory() && existsSync3(
|
|
39889
|
+
return existsSync3(candidate) && statSync4(candidate).isDirectory() && existsSync3(join3(candidate, "rules")) && existsSync3(join3(candidate, "workflows"));
|
|
39860
39890
|
}
|
|
39861
39891
|
function listBundledConfigFiles() {
|
|
39862
39892
|
const root = bundledConfigRoot();
|
|
@@ -39867,7 +39897,7 @@ function listBundledConfigFiles() {
|
|
|
39867
39897
|
function walk(dir, relPrefix, filter = /\.(ya?ml|json)$/i) {
|
|
39868
39898
|
const acc = [];
|
|
39869
39899
|
for (const entry of readdirSync3(dir)) {
|
|
39870
|
-
const abs =
|
|
39900
|
+
const abs = join3(dir, entry);
|
|
39871
39901
|
const rel = relPrefix.length > 0 ? `${relPrefix}/${entry}` : entry;
|
|
39872
39902
|
if (statSync4(abs).isDirectory()) {
|
|
39873
39903
|
acc.push(...walk(abs, rel, filter));
|
|
@@ -39905,7 +39935,7 @@ var init_template_renderer = () => {};
|
|
|
39905
39935
|
|
|
39906
39936
|
// ../../packages/config/src/executor-update.ts
|
|
39907
39937
|
import { chmod, lstat, open, readFile, rename, stat, unlink } from "fs/promises";
|
|
39908
|
-
import { basename, dirname as dirname4, join as
|
|
39938
|
+
import { basename, dirname as dirname4, join as join4 } from "path";
|
|
39909
39939
|
async function setProjectExecutorDisabled(projectRoot, executorName, disabled) {
|
|
39910
39940
|
if (executorName.length === 0) {
|
|
39911
39941
|
throw new ExecutorUpdateError("INVALID_CONFIG", "executor name must be a non-empty string");
|
|
@@ -39913,7 +39943,7 @@ async function setProjectExecutorDisabled(projectRoot, executorName, disabled) {
|
|
|
39913
39943
|
if (typeof disabled !== "boolean") {
|
|
39914
39944
|
throw new ExecutorUpdateError("INVALID_CONFIG", `disabled must be a boolean, received ${typeof disabled}`);
|
|
39915
39945
|
}
|
|
39916
|
-
const configPath =
|
|
39946
|
+
const configPath = join4(projectRoot, ".spur", "config.yaml");
|
|
39917
39947
|
let original;
|
|
39918
39948
|
try {
|
|
39919
39949
|
original = await stat(configPath);
|
|
@@ -39968,7 +39998,7 @@ async function setProjectExecutorDisabled(projectRoot, executorName, disabled) {
|
|
|
39968
39998
|
}
|
|
39969
39999
|
target.set("disabled", disabled);
|
|
39970
40000
|
const serialized = String(doc2);
|
|
39971
|
-
const tmpPath =
|
|
40001
|
+
const tmpPath = join4(dirname4(configPath), `.${basename(configPath)}.tmp-${process.pid}`);
|
|
39972
40002
|
try {
|
|
39973
40003
|
const latest = await stat(configPath);
|
|
39974
40004
|
if (latest.mtimeMs !== baseline.mtimeMs || latest.size !== baseline.size || latest.ino !== baseline.ino) {
|
|
@@ -40056,8 +40086,8 @@ var init_executor_update = __esm(() => {
|
|
|
40056
40086
|
|
|
40057
40087
|
// ../../packages/config/src/loader.ts
|
|
40058
40088
|
import { existsSync as existsSync4, statSync as statSync5 } from "fs";
|
|
40059
|
-
import { homedir as
|
|
40060
|
-
import { dirname as dirname5, join as
|
|
40089
|
+
import { homedir as homedir3 } from "os";
|
|
40090
|
+
import { dirname as dirname5, join as join5, resolve } from "path";
|
|
40061
40091
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
40062
40092
|
function embeddedSchemasId(embeddedSchemas) {
|
|
40063
40093
|
if (embeddedSchemas === undefined)
|
|
@@ -40080,7 +40110,7 @@ function cacheKey(configPath, opts, validateJsonSchema2) {
|
|
|
40080
40110
|
}
|
|
40081
40111
|
function resolveSchemaSpecifier(specifier, manifestSpecifier) {
|
|
40082
40112
|
if (specifier === manifestSpecifier) {
|
|
40083
|
-
const workspaceManifest =
|
|
40113
|
+
const workspaceManifest = join5(LOADER_DIR, "..", "..", "..", "apps", "cli", "package.json");
|
|
40084
40114
|
if (existsSync4(workspaceManifest))
|
|
40085
40115
|
return workspaceManifest;
|
|
40086
40116
|
}
|
|
@@ -40095,7 +40125,7 @@ function resolveConfigLayers(cwd) {
|
|
|
40095
40125
|
const layers = {};
|
|
40096
40126
|
const skipProject = cwd === undefined && process.env.SPUR_SKIP_PROJECT_CONFIG === "true";
|
|
40097
40127
|
if (!skipProject) {
|
|
40098
|
-
const projectConfig =
|
|
40128
|
+
const projectConfig = join5(cwd ?? process.cwd(), SPUR_CONFIG_DIR, SPUR_CONFIG_FILE);
|
|
40099
40129
|
if (existsSync4(projectConfig))
|
|
40100
40130
|
layers.project = projectConfig;
|
|
40101
40131
|
}
|
|
@@ -40168,9 +40198,9 @@ function assertNoGlobalFleetSection(globalRaw, layerPath, projectFile) {
|
|
|
40168
40198
|
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.`);
|
|
40169
40199
|
}
|
|
40170
40200
|
function assertNoLegacyFleetFile(projectRoot, projectConfigPath) {
|
|
40171
|
-
const candidates = new Set([
|
|
40201
|
+
const candidates = new Set([join5(projectRoot, SPUR_CONFIG_DIR, "fleet.json")]);
|
|
40172
40202
|
if (projectConfigPath !== undefined)
|
|
40173
|
-
candidates.add(
|
|
40203
|
+
candidates.add(join5(dirname5(projectConfigPath), "fleet.json"));
|
|
40174
40204
|
for (const legacyFile of candidates) {
|
|
40175
40205
|
if (!existsSync4(legacyFile))
|
|
40176
40206
|
continue;
|
|
@@ -40178,7 +40208,7 @@ function assertNoLegacyFleetFile(projectRoot, projectConfigPath) {
|
|
|
40178
40208
|
}
|
|
40179
40209
|
}
|
|
40180
40210
|
function projectConfigReplacement(layers) {
|
|
40181
|
-
return layers.project ??
|
|
40211
|
+
return layers.project ?? join5(SPUR_CONFIG_DIR, SPUR_CONFIG_FILE);
|
|
40182
40212
|
}
|
|
40183
40213
|
function schemaValidationContext(opts) {
|
|
40184
40214
|
const embeddedSchemas = opts?.embeddedSchemas;
|
|
@@ -40433,7 +40463,7 @@ async function resolvePlanningFolders(fs2) {
|
|
|
40433
40463
|
return promise2;
|
|
40434
40464
|
}
|
|
40435
40465
|
async function resolvePlanningFoldersUncached(fs2) {
|
|
40436
|
-
const configPath = fs2.resolve(
|
|
40466
|
+
const configPath = fs2.resolve(join5(SPUR_CONFIG_DIR, SPUR_CONFIG_FILE));
|
|
40437
40467
|
if (!await fs2.exists(configPath))
|
|
40438
40468
|
return defaultPlanningFolders();
|
|
40439
40469
|
try {
|
|
@@ -40472,7 +40502,7 @@ var init_loader = __esm(() => {
|
|
|
40472
40502
|
init_template_renderer();
|
|
40473
40503
|
init_executor_update();
|
|
40474
40504
|
init_projects();
|
|
40475
|
-
GLOBAL_CONFIG_FILE =
|
|
40505
|
+
GLOBAL_CONFIG_FILE = join5(homedir3(), ".config", "spur", "config.yaml");
|
|
40476
40506
|
LOADER_DIR = dirname5(fileURLToPath3(import.meta.url));
|
|
40477
40507
|
embeddedSchemasIds = new WeakMap;
|
|
40478
40508
|
spurConfigCache = new Map;
|
|
@@ -68002,7 +68032,7 @@ var init_schema3 = __esm(() => {
|
|
|
68002
68032
|
});
|
|
68003
68033
|
|
|
68004
68034
|
// ../../packages/domain/src/migrations.ts
|
|
68005
|
-
import { join as
|
|
68035
|
+
import { join as join7 } from "path";
|
|
68006
68036
|
async function allMigrationsJournaled(adapter, migrations) {
|
|
68007
68037
|
if (migrations.length === 0)
|
|
68008
68038
|
return true;
|
|
@@ -68068,6 +68098,7 @@ async function applyCliMigrations(adapter, migrations = CLI_MIGRATIONS) {
|
|
|
68068
68098
|
const runsStatusDoneSkip = migration.id === "0017_spur_cli_runs_status_completed_to_done" && (!await tableExists(adapter, "runs") || !await columnExists(adapter, "runs", "status"));
|
|
68069
68099
|
const historyBoardToolStatsColumnsSkip = migration.id === "0031_spur_cli_history_board_tool_stats_columns" && (!await tableExists(adapter, "history_board_tool_stats") || await columnExists(adapter, "history_board_tool_stats", "fresh_input_tokens"));
|
|
68070
68100
|
const queueJobsActiveIndexSkip = migration.id === "0027_spur_cli_history_refresh_active_unique" && !await tableExists(adapter, "queue_jobs");
|
|
68101
|
+
const schedulerCustomActiveIndexSkip = migration.id === "0047_spur_cli_scheduler_custom_active_unique" && !await tableExists(adapter, "queue_jobs");
|
|
68071
68102
|
const queueJobsDeadlineLeaseSkip = migration.id === "0041_spur_cli_queue_jobs_deadline_lease_columns" && !await tableExists(adapter, "queue_jobs");
|
|
68072
68103
|
const inboxRequestKeySkip = migration.id === "0043_spur_cli_inbox_messages_request_key" && !await tableExists(adapter, "inbox_messages");
|
|
68073
68104
|
const coordinationReceiptColumnsSkip = migration.id === "0044_spur_cli_coordination_runs_receipt_columns" && !await tableExists(adapter, "coordination_runs");
|
|
@@ -68076,7 +68107,7 @@ async function applyCliMigrations(adapter, migrations = CLI_MIGRATIONS) {
|
|
|
68076
68107
|
const historyRollupWatermarkSkip = migration.id === "0036_spur_cli_history_rollup_watermark" && (await tableExists(adapter, "history_board_rollup_watermark") || !await tableExists(adapter, "history_message") || !await columnExists(adapter, "history_message", "imported_at"));
|
|
68077
68108
|
const boundedRollupDerivationsSkip = migration.id === "0039_spur_cli_bounded_rollup_derivations" && (!await tableExists(adapter, "history_message") || !await columnExists(adapter, "history_message", "source_file") || !await tableExists(adapter, "history_board_source_daily"));
|
|
68078
68109
|
const historyModelFallbackIndexSkip = migration.id === "0042_spur_cli_history_model_fallback_index" && (!await tableExists(adapter, "history_message") || !await columnExists(adapter, "history_message", "model"));
|
|
68079
|
-
if (shouldApplySql && !sequenceIndexSkip && !argsRawSkip && !runsStatusDoneSkip && !nameOccurredIndexSkip && !historyBoardQueryIndexesSkip && !historyPerformanceIndexesSkip && !historyToolCallIndexesSkip && !historyBoardCoveringIndexesSkip && !historyBoardToolStatsColumnsSkip && !callIdSkip && !tsNullableSkip && !queueJobsActiveIndexSkip && !queueJobsDeadlineLeaseSkip && !inboxRequestKeySkip && !coordinationReceiptColumnsSkip && !historyToolIdentitySkip && !historyMeasureVectorSkip && !historyRollupWatermarkSkip && !boundedRollupDerivationsSkip && !historyModelFallbackIndexSkip) {
|
|
68110
|
+
if (shouldApplySql && !sequenceIndexSkip && !argsRawSkip && !runsStatusDoneSkip && !nameOccurredIndexSkip && !historyBoardQueryIndexesSkip && !historyPerformanceIndexesSkip && !historyToolCallIndexesSkip && !historyBoardCoveringIndexesSkip && !historyBoardToolStatsColumnsSkip && !callIdSkip && !tsNullableSkip && !queueJobsActiveIndexSkip && !schedulerCustomActiveIndexSkip && !queueJobsDeadlineLeaseSkip && !inboxRequestKeySkip && !coordinationReceiptColumnsSkip && !historyToolIdentitySkip && !historyMeasureVectorSkip && !historyRollupWatermarkSkip && !boundedRollupDerivationsSkip && !historyModelFallbackIndexSkip) {
|
|
68080
68111
|
for (const statement of splitSqlStatements(migration.sql)) {
|
|
68081
68112
|
await adapter.exec(statement);
|
|
68082
68113
|
}
|
|
@@ -68114,7 +68145,7 @@ async function loadSqlMigrations(folder) {
|
|
|
68114
68145
|
for (const entry of entries) {
|
|
68115
68146
|
migrations.push({
|
|
68116
68147
|
id: entry.replace(/\.sql$/, ""),
|
|
68117
|
-
sql: await fs3.readFile(
|
|
68148
|
+
sql: await fs3.readFile(join7(folder, entry))
|
|
68118
68149
|
});
|
|
68119
68150
|
}
|
|
68120
68151
|
return migrations.length > 0 ? migrations : CLI_MIGRATIONS;
|
|
@@ -68189,6 +68220,15 @@ CREATE INDEX IF NOT EXISTS queue_jobs_ready_idx ON queue_jobs (status, next_retr
|
|
|
68189
68220
|
-- jobs that legitimately have multiple active rows concurrently, so a global
|
|
68190
68221
|
-- (type, status) unique index would break them.
|
|
68191
68222
|
CREATE UNIQUE INDEX IF NOT EXISTS queue_jobs_history_refresh_active_unique ON queue_jobs (type) WHERE type = 'history.refresh' AND status IN ('pending', 'processing');
|
|
68223
|
+
|
|
68224
|
+
-- At most one ACTIVE configured-scheduler job per job NAME (task 0863): the tick
|
|
68225
|
+
-- reads findActiveSchedulerCustomJob and then inserts, which is not atomic across
|
|
68226
|
+
-- spur serve daemons sharing one project DB \u2014 every daemon's cron occurrence used
|
|
68227
|
+
-- to insert its own row (3-14 per tick on 2026-09-14/15). The name lives inside the
|
|
68228
|
+
-- JSON payload, so this is an expression index \u2014 scoped to scheduler.custom +
|
|
68229
|
+
-- active rows exactly like the 0716 history.refresh index, so terminal rows and
|
|
68230
|
+
-- other job types (task-action, feature-action, history.refresh) stay unaffected.
|
|
68231
|
+
CREATE UNIQUE INDEX IF NOT EXISTS queue_jobs_scheduler_custom_active_unique ON queue_jobs (json_extract(payload, '$.name')) WHERE type = 'scheduler.custom' AND status IN ('pending', 'processing');
|
|
68192
68232
|
`, SYSTEM_EVENTS_SCHEMA_SQL = `
|
|
68193
68233
|
CREATE TABLE IF NOT EXISTS system_events (
|
|
68194
68234
|
id TEXT PRIMARY KEY,
|
|
@@ -68630,6 +68670,25 @@ CREATE TABLE IF NOT EXISTS agent_instances (
|
|
|
68630
68670
|
CREATE INDEX IF NOT EXISTS idx_agent_instances_role ON agent_instances (role);
|
|
68631
68671
|
CREATE INDEX IF NOT EXISTS idx_agent_instances_executor ON agent_instances (executor);
|
|
68632
68672
|
CREATE INDEX IF NOT EXISTS idx_agent_instances_team ON agent_instances (team_id);
|
|
68673
|
+
`, SCHEDULER_CUSTOM_ACTIVE_UNIQUE_SCHEMA_SQL = `
|
|
68674
|
+
UPDATE queue_jobs
|
|
68675
|
+
SET status = 'failed',
|
|
68676
|
+
last_error = 'retired by migration 0047_spur_cli_scheduler_custom_active_unique: superseded duplicate active scheduler.custom job name',
|
|
68677
|
+
processing_at = NULL,
|
|
68678
|
+
updated_at = CAST(strftime('%s', 'now') AS INTEGER) * 1000
|
|
68679
|
+
WHERE type = 'scheduler.custom'
|
|
68680
|
+
AND status IN ('pending', 'processing')
|
|
68681
|
+
AND id NOT IN (
|
|
68682
|
+
SELECT id FROM (
|
|
68683
|
+
SELECT id, ROW_NUMBER() OVER (
|
|
68684
|
+
PARTITION BY json_extract(payload, '$.name') ORDER BY created_at ASC, id ASC
|
|
68685
|
+
) AS rank
|
|
68686
|
+
FROM queue_jobs
|
|
68687
|
+
WHERE type = 'scheduler.custom' AND status IN ('pending', 'processing')
|
|
68688
|
+
) WHERE rank = 1
|
|
68689
|
+
);
|
|
68690
|
+
|
|
68691
|
+
CREATE UNIQUE INDEX IF NOT EXISTS queue_jobs_scheduler_custom_active_unique ON queue_jobs (json_extract(payload, '$.name')) WHERE type = 'scheduler.custom' AND status IN ('pending', 'processing');
|
|
68633
68692
|
`, HISTORY_REFRESH_ACTIVE_UNIQUE_SCHEMA_SQL = `
|
|
68634
68693
|
UPDATE queue_jobs
|
|
68635
68694
|
SET status = 'failed',
|
|
@@ -69031,6 +69090,10 @@ ${HISTORY_TASK_SESSION_SCHEMA_SQL}
|
|
|
69031
69090
|
{
|
|
69032
69091
|
id: "0046_spur_cli_project_strategy",
|
|
69033
69092
|
sql: PROJECT_STRATEGY_SCHEMA_SQL
|
|
69093
|
+
},
|
|
69094
|
+
{
|
|
69095
|
+
id: "0047_spur_cli_scheduler_custom_active_unique",
|
|
69096
|
+
sql: SCHEDULER_CUSTOM_ACTIVE_UNIQUE_SCHEMA_SQL
|
|
69034
69097
|
}
|
|
69035
69098
|
];
|
|
69036
69099
|
});
|
|
@@ -70769,7 +70832,7 @@ var init_envelope = __esm(() => {
|
|
|
70769
70832
|
|
|
70770
70833
|
// ../../packages/domain/src/retention.ts
|
|
70771
70834
|
import { readdirSync as readdirSync5, rmSync as rmSync3, statfsSync, statSync as statSync6 } from "fs";
|
|
70772
|
-
import { join as
|
|
70835
|
+
import { join as join8 } from "path";
|
|
70773
70836
|
async function estimateReclaimableBytes(db2) {
|
|
70774
70837
|
try {
|
|
70775
70838
|
const rows = await db2.queryAll("SELECT pageno, pagetype, ncell, payload, unused FROM dbstat ORDER BY pageno");
|
|
@@ -70837,7 +70900,7 @@ async function runRetention(db2, cwd, now = new Date) {
|
|
|
70837
70900
|
result.queueJobs = await purgeQueueJobs(db2, now);
|
|
70838
70901
|
result.ledgerRows = await purgeLedger(db2, now);
|
|
70839
70902
|
result.backupFiles = pruneBackups(cwd, now);
|
|
70840
|
-
result.compaction = await compactDatabase(db2, { dbPath:
|
|
70903
|
+
result.compaction = await compactDatabase(db2, { dbPath: join8(cwd, ".spur", "spur.db"), now });
|
|
70841
70904
|
return result;
|
|
70842
70905
|
}
|
|
70843
70906
|
async function purgeRuleEvalRuns(db2, now) {
|
|
@@ -70850,7 +70913,7 @@ async function purgeLedger(db2, now) {
|
|
|
70850
70913
|
return purgeRows(db2, `DELETE FROM history_import_ledger WHERE imported_at < ?`, new Date(now.getTime() - LEDGER_RETENTION_DAYS * DAY_MS).toISOString());
|
|
70851
70914
|
}
|
|
70852
70915
|
function pruneBackups(cwd, now) {
|
|
70853
|
-
const dir =
|
|
70916
|
+
const dir = join8(cwd, ".spur", "backups");
|
|
70854
70917
|
let entries;
|
|
70855
70918
|
try {
|
|
70856
70919
|
entries = readdirSync5(dir);
|
|
@@ -70860,7 +70923,7 @@ function pruneBackups(cwd, now) {
|
|
|
70860
70923
|
const cutoffMs = now.getTime() - BACKUP_RETENTION_DAYS * DAY_MS;
|
|
70861
70924
|
let reclaimed = 0;
|
|
70862
70925
|
for (const name of entries) {
|
|
70863
|
-
const path9 =
|
|
70926
|
+
const path9 = join8(dir, name);
|
|
70864
70927
|
try {
|
|
70865
70928
|
const stat2 = statSync6(path9);
|
|
70866
70929
|
if (!stat2.isFile() || stat2.mtimeMs >= cutoffMs)
|
|
@@ -70945,12 +71008,12 @@ var init_maintenance = __esm(() => {
|
|
|
70945
71008
|
|
|
70946
71009
|
// ../../packages/domain/src/planning/locks.ts
|
|
70947
71010
|
import { closeSync, fsyncSync, mkdirSync as mkdirSync4, openSync, readFileSync as readFileSync7, renameSync as renameSync3, unlinkSync, writeSync } from "fs";
|
|
70948
|
-
import { dirname as dirname8, join as
|
|
71011
|
+
import { dirname as dirname8, join as join9 } from "path";
|
|
70949
71012
|
function lockPathForEntity(folder, entityId) {
|
|
70950
|
-
return
|
|
71013
|
+
return join9(folder, `.${entityId}.lock`);
|
|
70951
71014
|
}
|
|
70952
71015
|
function createLockPath(folder) {
|
|
70953
|
-
return
|
|
71016
|
+
return join9(folder, ".create.lock");
|
|
70954
71017
|
}
|
|
70955
71018
|
function parseLockContent(raw) {
|
|
70956
71019
|
const str = typeof raw === "string" ? raw : raw.toString();
|
|
@@ -71055,7 +71118,7 @@ function fsyncPath(path9, swallowErrors = false) {
|
|
|
71055
71118
|
function atomicWrite(dest, content, entityId, fs3, projectName = "spur") {
|
|
71056
71119
|
const dir = dirname8(dest);
|
|
71057
71120
|
const rand = Math.random().toString(36).slice(2, 10);
|
|
71058
|
-
const tempPath =
|
|
71121
|
+
const tempPath = join9(dir, `.${projectName}.${entityId}.${rand}.tmp`);
|
|
71059
71122
|
fs3.writeFile(tempPath, content);
|
|
71060
71123
|
fsyncPath(tempPath);
|
|
71061
71124
|
renameSync3(tempPath, dest);
|
|
@@ -71064,7 +71127,7 @@ function atomicWrite(dest, content, entityId, fs3, projectName = "spur") {
|
|
|
71064
71127
|
async function atomicWriteAsync(dest, content, entityId, fs3, projectName = "spur") {
|
|
71065
71128
|
const dir = dirname8(dest);
|
|
71066
71129
|
const rand = Math.random().toString(36).slice(2, 10);
|
|
71067
|
-
const tempPath =
|
|
71130
|
+
const tempPath = join9(dir, `.${projectName}.${entityId}.${rand}.tmp`);
|
|
71068
71131
|
await fs3.writeFile(tempPath, content);
|
|
71069
71132
|
fsyncPath(tempPath);
|
|
71070
71133
|
renameSync3(tempPath, dest);
|
|
@@ -71079,7 +71142,7 @@ function cleanupTempFiles(folder, entityId, fs3, projectName = "spur") {
|
|
|
71079
71142
|
let count3 = 0;
|
|
71080
71143
|
for (const entry of entries) {
|
|
71081
71144
|
if (entry.startsWith(prefix) && entry.endsWith(suffix)) {
|
|
71082
|
-
fs3.deleteFile(
|
|
71145
|
+
fs3.deleteFile(join9(folder, entry));
|
|
71083
71146
|
count3++;
|
|
71084
71147
|
}
|
|
71085
71148
|
}
|
|
@@ -71660,6 +71723,7 @@ var init_schema6 = __esm(() => {
|
|
|
71660
71723
|
done_forced: exports_external.preprocess((v) => typeof v === "string" ? v === "true" : v, exports_external.boolean().optional()),
|
|
71661
71724
|
done_reason: exports_external.string().optional(),
|
|
71662
71725
|
feature_link_declined: exports_external.preprocess((v) => typeof v === "string" ? v === "true" : v, exports_external.boolean().optional()),
|
|
71726
|
+
estimate_hours: exports_external.preprocess((v) => typeof v === "string" && v.trim() !== "" ? Number(v) : v, exports_external.number().positive().optional()),
|
|
71663
71727
|
created_at: isoDateString,
|
|
71664
71728
|
updated_at: isoDateString
|
|
71665
71729
|
});
|
|
@@ -71687,7 +71751,8 @@ var init_schema6 = __esm(() => {
|
|
|
71687
71751
|
parent_wbs: wbsString,
|
|
71688
71752
|
priority: exports_external.enum(PRIORITIES).optional(),
|
|
71689
71753
|
tags: exports_external.array(exports_external.string()).optional(),
|
|
71690
|
-
template: exports_external.enum(TASK_VARIANTS).optional()
|
|
71754
|
+
template: exports_external.enum(TASK_VARIANTS).optional(),
|
|
71755
|
+
estimate_hours: exports_external.number().positive().optional()
|
|
71691
71756
|
}).strict();
|
|
71692
71757
|
taskBatchSchema = exports_external.array(taskBatchItemSchema).min(1, "batch must contain at least one task");
|
|
71693
71758
|
});
|
|
@@ -73012,7 +73077,7 @@ var init_escalation_packet = __esm(() => {
|
|
|
73012
73077
|
|
|
73013
73078
|
// ../../packages/app/src/observability/escalation-packet-sink.ts
|
|
73014
73079
|
import { randomUUID } from "crypto";
|
|
73015
|
-
import { join as
|
|
73080
|
+
import { join as join10 } from "path";
|
|
73016
73081
|
function parseDryRunProbeMetadata(metadataJson) {
|
|
73017
73082
|
if (metadataJson === "")
|
|
73018
73083
|
return false;
|
|
@@ -73059,7 +73124,7 @@ class EscalationPacketSink {
|
|
|
73059
73124
|
}
|
|
73060
73125
|
}
|
|
73061
73126
|
async readPacket(runId) {
|
|
73062
|
-
const path9 =
|
|
73127
|
+
const path9 = join10(this.options.cwd, ".spur", "run", `${runId}-escalation.json`);
|
|
73063
73128
|
if (!await this.options.fs.exists(path9))
|
|
73064
73129
|
return;
|
|
73065
73130
|
return JSON.parse(await this.options.fs.readFile(path9));
|
|
@@ -73127,7 +73192,7 @@ class EscalationPacketSink {
|
|
|
73127
73192
|
const identity6 = await this.resolveIdentity(wbs);
|
|
73128
73193
|
const now2 = (this.options.now ?? (() => new Date().toISOString()))();
|
|
73129
73194
|
const packet = buildEscalationPacket({ ...packetInput, identity: identity6, now: now2 });
|
|
73130
|
-
const path9 =
|
|
73195
|
+
const path9 = join10(this.options.cwd, ".spur", "run", `${input.runId}-escalation.json`);
|
|
73131
73196
|
if (!await this.options.fs.exists(path9)) {
|
|
73132
73197
|
await this.options.fs.writeFile(path9, `${JSON.stringify(packet, null, 2)}
|
|
73133
73198
|
`);
|
|
@@ -73418,7 +73483,7 @@ var init_step_reporter = () => {};
|
|
|
73418
73483
|
|
|
73419
73484
|
// ../../packages/app/src/observability/workflow-run-log-sink.ts
|
|
73420
73485
|
import { closeSync as closeSync2, mkdirSync as mkdirSync5, openSync as openSync2, writeSync as writeSync2 } from "fs";
|
|
73421
|
-
import { join as
|
|
73486
|
+
import { join as join11 } from "path";
|
|
73422
73487
|
|
|
73423
73488
|
class WorkflowRunLogSink {
|
|
73424
73489
|
filePath;
|
|
@@ -73434,7 +73499,7 @@ class WorkflowRunLogSink {
|
|
|
73434
73499
|
bus;
|
|
73435
73500
|
handlers;
|
|
73436
73501
|
constructor(options) {
|
|
73437
|
-
this.filePath =
|
|
73502
|
+
this.filePath = join11(options.dir, `${options.runId}.log`);
|
|
73438
73503
|
this.maxBytes = options.maxBytes ?? DEFAULT_RUN_LOG_MAX_BYTES;
|
|
73439
73504
|
this.maxLines = options.maxLines;
|
|
73440
73505
|
this.planPreview = options.planPreview;
|
|
@@ -73889,7 +73954,7 @@ var init_failure_classification = __esm(() => {
|
|
|
73889
73954
|
});
|
|
73890
73955
|
|
|
73891
73956
|
// ../../packages/app/src/services/run-session-observer.ts
|
|
73892
|
-
import { homedir as
|
|
73957
|
+
import { homedir as homedir4 } from "os";
|
|
73893
73958
|
import { basename as basename2, isAbsolute as isAbsolute2, resolve as resolve3 } from "path";
|
|
73894
73959
|
|
|
73895
73960
|
class RunSessionObserver {
|
|
@@ -73901,7 +73966,7 @@ class RunSessionObserver {
|
|
|
73901
73966
|
now;
|
|
73902
73967
|
constructor(options) {
|
|
73903
73968
|
this.options = options;
|
|
73904
|
-
this.home = options.home ??
|
|
73969
|
+
this.home = options.home ?? homedir4();
|
|
73905
73970
|
this.cwd = options.cwd ?? process.cwd();
|
|
73906
73971
|
this.now = options.now ?? Date.now;
|
|
73907
73972
|
}
|
|
@@ -74113,7 +74178,7 @@ var init_run_session_observer = __esm(() => {
|
|
|
74113
74178
|
|
|
74114
74179
|
// ../../packages/app/src/services/agent-service.ts
|
|
74115
74180
|
import { createHash as createHash8 } from "crypto";
|
|
74116
|
-
import { homedir as
|
|
74181
|
+
import { homedir as homedir5 } from "os";
|
|
74117
74182
|
import { isatty as isatty3 } from "tty";
|
|
74118
74183
|
|
|
74119
74184
|
class AgentService {
|
|
@@ -74531,7 +74596,7 @@ class AgentService {
|
|
|
74531
74596
|
getDb,
|
|
74532
74597
|
output: this.ctx.output,
|
|
74533
74598
|
registry: this.sessionRootRegistry,
|
|
74534
|
-
home:
|
|
74599
|
+
home: homedir5(),
|
|
74535
74600
|
cwd: cwd || this.ctx.cwd,
|
|
74536
74601
|
json: jsonOutput
|
|
74537
74602
|
}));
|
|
@@ -75635,15 +75700,15 @@ var init_agent_service = __esm(() => {
|
|
|
75635
75700
|
// ../../packages/app/src/services/project-registry.ts
|
|
75636
75701
|
import { existsSync as existsSync5, mkdirSync as mkdirSync6, readFileSync as readFileSync8, realpathSync as realpathSync3, renameSync as renameSync4, rmSync as rmSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
75637
75702
|
import { connect, createServer } from "net";
|
|
75638
|
-
import { homedir as
|
|
75639
|
-
import { dirname as dirname9, join as
|
|
75703
|
+
import { homedir as homedir6 } from "os";
|
|
75704
|
+
import { dirname as dirname9, join as join12, resolve as resolve4 } from "path";
|
|
75640
75705
|
function normalizeProjectPath(pathInput) {
|
|
75641
75706
|
const trimmed = pathInput.trim();
|
|
75642
75707
|
let expanded = trimmed;
|
|
75643
75708
|
if (trimmed === "~") {
|
|
75644
|
-
expanded =
|
|
75709
|
+
expanded = homedir6();
|
|
75645
75710
|
} else if (trimmed.startsWith("~/")) {
|
|
75646
|
-
expanded =
|
|
75711
|
+
expanded = join12(homedir6(), trimmed.slice(2));
|
|
75647
75712
|
} else {
|
|
75648
75713
|
expanded = resolve4(trimmed);
|
|
75649
75714
|
}
|
|
@@ -75944,7 +76009,7 @@ var init_project_registry = __esm(() => {
|
|
|
75944
76009
|
});
|
|
75945
76010
|
|
|
75946
76011
|
// ../../packages/app/src/services/fleet-service.ts
|
|
75947
|
-
import { basename as basename3, dirname as dirname10, join as
|
|
76012
|
+
import { basename as basename3, dirname as dirname10, join as join13 } from "path";
|
|
75948
76013
|
function resolveMemberExecutor(params) {
|
|
75949
76014
|
const { member, index: index2, label, agentConfig, roles, roster } = params;
|
|
75950
76015
|
if (member.executor !== undefined) {
|
|
@@ -76154,7 +76219,7 @@ class FleetService {
|
|
|
76154
76219
|
}
|
|
76155
76220
|
const slug = await this.projectSlug(normalized);
|
|
76156
76221
|
const config4 = await this.effectiveConfig();
|
|
76157
|
-
const configDir =
|
|
76222
|
+
const configDir = join13(normalized, ".spur", "agents");
|
|
76158
76223
|
const specs = await loadAgentSpecs(configDir);
|
|
76159
76224
|
const projection = materializeRoster({
|
|
76160
76225
|
slug,
|
|
@@ -76208,7 +76273,7 @@ class FleetService {
|
|
|
76208
76273
|
}
|
|
76209
76274
|
async assertLaunchGroundTruth(projectPath) {
|
|
76210
76275
|
const expected = normalizeProjectPath(projectPath);
|
|
76211
|
-
const expectedStorageRoot =
|
|
76276
|
+
const expectedStorageRoot = join13(expected, ".spur");
|
|
76212
76277
|
const cwd = normalizeProjectPath(process.cwd());
|
|
76213
76278
|
const storageRoot = normalizeProjectPath(this.fs.resolve(".spur"));
|
|
76214
76279
|
const databasePath = this.ctx.openDb ? await new ProjectClaimDao(await this.ctx.openDb(expected)).databasePath() : "";
|
|
@@ -76316,7 +76381,7 @@ var init_task_locator = __esm(() => {
|
|
|
76316
76381
|
});
|
|
76317
76382
|
|
|
76318
76383
|
// ../../packages/app/src/services/agent-coordination-service.ts
|
|
76319
|
-
import { join as
|
|
76384
|
+
import { join as join14 } from "path";
|
|
76320
76385
|
|
|
76321
76386
|
class AgentCoordinationService {
|
|
76322
76387
|
ctx;
|
|
@@ -76324,7 +76389,7 @@ class AgentCoordinationService {
|
|
|
76324
76389
|
orchestratorPromise;
|
|
76325
76390
|
constructor(ctx) {
|
|
76326
76391
|
this.ctx = ctx;
|
|
76327
|
-
this.configDir =
|
|
76392
|
+
this.configDir = join14(ctx.cwd, ".spur", "agents");
|
|
76328
76393
|
}
|
|
76329
76394
|
async sendMessage(fromId, toId, body, replyTo, requestKey) {
|
|
76330
76395
|
validateAgentId(toId);
|
|
@@ -76590,7 +76655,7 @@ class AgentCoordinationService {
|
|
|
76590
76655
|
async resolveTaskFile(taskId) {
|
|
76591
76656
|
const fs3 = this.ctx.fs;
|
|
76592
76657
|
const { foldersConfig } = await resolvePlanningFolders(fs3);
|
|
76593
|
-
const dirs = [...new Set([foldersConfig.active_folder, ...Object.keys(foldersConfig.folders)])].map((dir) =>
|
|
76658
|
+
const dirs = [...new Set([foldersConfig.active_folder, ...Object.keys(foldersConfig.folders)])].map((dir) => join14(this.ctx.cwd, dir));
|
|
76594
76659
|
return await TaskLocator.forDirs(fs3, dirs).findPathByWbs(taskId);
|
|
76595
76660
|
}
|
|
76596
76661
|
}
|
|
@@ -76942,7 +77007,7 @@ var init_agent_quota_updates = __esm(() => {
|
|
|
76942
77007
|
});
|
|
76943
77008
|
|
|
76944
77009
|
// ../../packages/app/src/services/anchor-qualifier.ts
|
|
76945
|
-
import { basename as basename4, join as
|
|
77010
|
+
import { basename as basename4, join as join15 } from "path";
|
|
76946
77011
|
async function resolveRepoRoot(projectRoot, hintDir) {
|
|
76947
77012
|
if (projectRoot)
|
|
76948
77013
|
return projectRoot;
|
|
@@ -77071,7 +77136,7 @@ async function qualifyAnchors(fs3, opts) {
|
|
|
77071
77136
|
} catch {
|
|
77072
77137
|
continue;
|
|
77073
77138
|
}
|
|
77074
|
-
const mdFiles = entries.filter((name) => name.endsWith(".md") && name !== "kanban.md").map((name) =>
|
|
77139
|
+
const mdFiles = entries.filter((name) => name.endsWith(".md") && name !== "kanban.md").map((name) => join15(dir, name)).sort();
|
|
77075
77140
|
for (const filePath of mdFiles) {
|
|
77076
77141
|
let raw;
|
|
77077
77142
|
try {
|
|
@@ -78293,7 +78358,7 @@ __export(exports_feature_check, {
|
|
|
78293
78358
|
FeatureCheckService: () => FeatureCheckService,
|
|
78294
78359
|
DEFAULT_FEATURE_MATRIX: () => DEFAULT_FEATURE_MATRIX
|
|
78295
78360
|
});
|
|
78296
|
-
import { dirname as dirname11, join as
|
|
78361
|
+
import { dirname as dirname11, join as join16 } from "path";
|
|
78297
78362
|
function isGroupFeature(fm) {
|
|
78298
78363
|
const tags = fm.tags;
|
|
78299
78364
|
return Array.isArray(tags) && tags.includes("group") ? "group" : "standard";
|
|
@@ -78391,9 +78456,9 @@ function decodeVerdictRows(source, sectionName, required2) {
|
|
|
78391
78456
|
function defaultVerdictRunDir(tasksDir) {
|
|
78392
78457
|
const norm = tasksDir.replace(/\\/g, "/");
|
|
78393
78458
|
if (/\/docs\/tasks\d*$/.test(norm) || /\/docs\/tasks$/.test(norm)) {
|
|
78394
|
-
return
|
|
78459
|
+
return join16(dirname11(dirname11(tasksDir)), ".spur", "run");
|
|
78395
78460
|
}
|
|
78396
|
-
return
|
|
78461
|
+
return join16(dirname11(tasksDir), ".spur", "run");
|
|
78397
78462
|
}
|
|
78398
78463
|
function rowMatchesScenario(id, sc) {
|
|
78399
78464
|
const stripped = id.replace(/^\[[^\]]*\]\s*/, "").replace(/^Scenario:\s*/i, "").replace(/^\[[^\]]*\]\s*/, "").trim();
|
|
@@ -78523,7 +78588,7 @@ var init_feature_check = __esm(() => {
|
|
|
78523
78588
|
await this.checkOneActiveGoal(fm, featureId2, options.featuresDir, findings, options.asStatus);
|
|
78524
78589
|
await this.checkChildrenLimit(featureId2, options.featuresDir, findings);
|
|
78525
78590
|
}
|
|
78526
|
-
const dogfoodDir = options?.dogfoodDir ?? (options?.featuresDir ?
|
|
78591
|
+
const dogfoodDir = options?.dogfoodDir ?? (options?.featuresDir ? join16(dirname11(options.featuresDir), "dogfood") : undefined);
|
|
78527
78592
|
const primaryTasksDir = options?.tasksDir ?? options?.tasksDirs?.[0];
|
|
78528
78593
|
const runDir = options?.runDir ?? (primaryTasksDir ? defaultVerdictRunDir(primaryTasksDir) : undefined);
|
|
78529
78594
|
const taskScanDirs = options?.tasksDirs && options.tasksDirs.length > 0 ? options.tasksDirs : options?.tasksDir ? [options.tasksDir] : [];
|
|
@@ -78954,7 +79019,7 @@ var init_feature_check = __esm(() => {
|
|
|
78954
79019
|
});
|
|
78955
79020
|
|
|
78956
79021
|
// ../../packages/app/src/services/task-check.ts
|
|
78957
|
-
import { basename as basename5, dirname as dirname12, join as
|
|
79022
|
+
import { basename as basename5, dirname as dirname12, join as join17 } from "path";
|
|
78958
79023
|
function isPlaceholderBody(body) {
|
|
78959
79024
|
const stripped = body.replace(/<!--[\s\S]*?-->/g, "").replace(/^\s*>\s*TBD\s*$/gim, "").trim();
|
|
78960
79025
|
return stripped.length === 0;
|
|
@@ -79214,7 +79279,7 @@ var init_task_check = __esm(() => {
|
|
|
79214
79279
|
this.runL2(doc2, entry, findings, raw);
|
|
79215
79280
|
this.runL3(doc2, entry, effectiveStatus, findings, isTransitionTarget);
|
|
79216
79281
|
const tasksDir = dirname12(filePath);
|
|
79217
|
-
const featuresDir =
|
|
79282
|
+
const featuresDir = join17(dirname12(tasksDir), "features");
|
|
79218
79283
|
await this.runL4(doc2, fm, effectiveStatus, findings, featuresDir, tasksDir, wbs);
|
|
79219
79284
|
await this.runL4Rollup(doc2, wbs, effectiveStatus, findings, tasksDir);
|
|
79220
79285
|
if (effectiveStatus !== "done" && effectiveStatus !== "cancelled") {
|
|
@@ -79781,7 +79846,7 @@ var init_task_check = __esm(() => {
|
|
|
79781
79846
|
for (const cite of citations) {
|
|
79782
79847
|
if (reported >= 5)
|
|
79783
79848
|
break;
|
|
79784
|
-
const abs =
|
|
79849
|
+
const abs = join17(projectRoot, cite.path);
|
|
79785
79850
|
let exists2 = false;
|
|
79786
79851
|
try {
|
|
79787
79852
|
exists2 = await this.fs.exists(abs);
|
|
@@ -79865,7 +79930,7 @@ var init_task_check = __esm(() => {
|
|
|
79865
79930
|
continue;
|
|
79866
79931
|
}
|
|
79867
79932
|
try {
|
|
79868
|
-
const abs =
|
|
79933
|
+
const abs = join17(dir, name);
|
|
79869
79934
|
const stat2 = await this.fs.stat(abs);
|
|
79870
79935
|
if (stat2 !== null && stat2 !== undefined && stat2.isDirectory?.())
|
|
79871
79936
|
stack.push(abs);
|
|
@@ -79960,7 +80025,7 @@ var init_task_check = __esm(() => {
|
|
|
79960
80025
|
}
|
|
79961
80026
|
async checkVerdictArtifact(wbs, tasksDir, findings) {
|
|
79962
80027
|
const projectRoot = resolveProjectRootFromTasksDir(tasksDir);
|
|
79963
|
-
const runDir =
|
|
80028
|
+
const runDir = join17(projectRoot, ".spur", "run");
|
|
79964
80029
|
const verdictPath = `${runDir}/${wbs}-verdict.json`;
|
|
79965
80030
|
const outcome = await readVerifyVerdict(this.fs, verdictPath, wbs);
|
|
79966
80031
|
if (outcome.kind === "missing")
|
|
@@ -80005,13 +80070,13 @@ var init_task_check = __esm(() => {
|
|
|
80005
80070
|
});
|
|
80006
80071
|
|
|
80007
80072
|
// ../../packages/app/src/services/corpus-check.ts
|
|
80008
|
-
import { basename as basename6, dirname as dirname13, join as
|
|
80073
|
+
import { basename as basename6, dirname as dirname13, join as join18, relative, resolve as resolve5 } from "path";
|
|
80009
80074
|
async function loadTaskMatrix(projectRoot) {
|
|
80010
80075
|
const fs3 = createNodeFileSystem3(projectRoot);
|
|
80011
|
-
const candidates = [
|
|
80076
|
+
const candidates = [join18(projectRoot, ".spur", "tasks", "section-matrix.yaml")];
|
|
80012
80077
|
const bundledRoot = bundledConfigRoot();
|
|
80013
80078
|
if (bundledRoot !== null)
|
|
80014
|
-
candidates.push(
|
|
80079
|
+
candidates.push(join18(bundledRoot, "tasks", "section-matrix.yaml"));
|
|
80015
80080
|
let matrixPath;
|
|
80016
80081
|
for (const candidate of candidates) {
|
|
80017
80082
|
if (await fs3.exists(candidate)) {
|
|
@@ -80054,7 +80119,7 @@ async function structuralSweep(projectRoot) {
|
|
|
80054
80119
|
const wbs = fileName.match(/^(\d{4})_.+\.md$/)?.[1];
|
|
80055
80120
|
if (wbs === undefined)
|
|
80056
80121
|
continue;
|
|
80057
|
-
const result = await taskService.check(
|
|
80122
|
+
const result = await taskService.check(join18(tasksDir, fileName), wbs);
|
|
80058
80123
|
for (const finding of result.findings) {
|
|
80059
80124
|
findings.push({
|
|
80060
80125
|
kind: "task",
|
|
@@ -80072,7 +80137,7 @@ async function structuralSweep(projectRoot) {
|
|
|
80072
80137
|
const id = fileName.match(/^([A-Z][0-9]*)_.+\.md$/)?.[1];
|
|
80073
80138
|
if (id === undefined)
|
|
80074
80139
|
continue;
|
|
80075
|
-
const result = await featureService.check(
|
|
80140
|
+
const result = await featureService.check(join18(featuresDir, fileName), id, {
|
|
80076
80141
|
featuresDir,
|
|
80077
80142
|
tasksDir: activeTasksDir,
|
|
80078
80143
|
tasksDirs: taskDirs,
|
|
@@ -80101,7 +80166,7 @@ async function duplicateIds(cwd, taskDirs, featuresDir) {
|
|
|
80101
80166
|
return [];
|
|
80102
80167
|
}
|
|
80103
80168
|
const pattern = kind === "task" ? /^(\d{4})_/ : /^([A-Z][0-9]*)_/;
|
|
80104
|
-
return names.map((n2) => ({ m: n2.match(pattern), n: n2 })).filter((x) => x.m !== null).map((x) => ({ id: x.m[1], file: relative(cwd,
|
|
80169
|
+
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 }));
|
|
80105
80170
|
};
|
|
80106
80171
|
const all = [
|
|
80107
80172
|
...(await Promise.all(taskDirs.map((dir) => scan(dir, "task")))).flat(),
|
|
@@ -80155,7 +80220,7 @@ function resolveProjectRoot(cwd) {
|
|
|
80155
80220
|
const fs3 = createNodeFileSystem3(cwd);
|
|
80156
80221
|
let current = resolve5(cwd);
|
|
80157
80222
|
while (true) {
|
|
80158
|
-
if (fs3.exists(
|
|
80223
|
+
if (fs3.exists(join18(current, ".spur", "config.yaml"))) {
|
|
80159
80224
|
return current;
|
|
80160
80225
|
}
|
|
80161
80226
|
const parent = dirname13(current);
|
|
@@ -80177,14 +80242,14 @@ function diskReader(cwd) {
|
|
|
80177
80242
|
return {
|
|
80178
80243
|
list: async (dir) => {
|
|
80179
80244
|
try {
|
|
80180
|
-
return (await fs3.readDir(
|
|
80245
|
+
return (await fs3.readDir(join18(cwd, dir))).map((n2) => `${dir}/${n2}`);
|
|
80181
80246
|
} catch {
|
|
80182
80247
|
return [];
|
|
80183
80248
|
}
|
|
80184
80249
|
},
|
|
80185
80250
|
read: async (path9) => {
|
|
80186
80251
|
try {
|
|
80187
|
-
return await fs3.readFile(
|
|
80252
|
+
return await fs3.readFile(join18(cwd, path9));
|
|
80188
80253
|
} catch {
|
|
80189
80254
|
return null;
|
|
80190
80255
|
}
|
|
@@ -80344,7 +80409,7 @@ var init_corpus_check = __esm(() => {
|
|
|
80344
80409
|
});
|
|
80345
80410
|
|
|
80346
80411
|
// ../../packages/app/src/services/corpus-migrator.ts
|
|
80347
|
-
import { join as
|
|
80412
|
+
import { join as join19, relative as relative2 } from "path";
|
|
80348
80413
|
function applyM1(data, flags) {
|
|
80349
80414
|
const raw = data.status;
|
|
80350
80415
|
if (typeof raw !== "string")
|
|
@@ -80644,7 +80709,7 @@ class CorpusMigrator {
|
|
|
80644
80709
|
}
|
|
80645
80710
|
async discoverFiles() {
|
|
80646
80711
|
const entries = await this.fs.readDir(this.corpusDir);
|
|
80647
|
-
return entries.filter((name) => name.endsWith(".md")).filter((name) => name !== "kanban.md").map((name) =>
|
|
80712
|
+
return entries.filter((name) => name.endsWith(".md")).filter((name) => name !== "kanban.md").map((name) => join19(this.corpusDir, name)).sort();
|
|
80648
80713
|
}
|
|
80649
80714
|
async migrateFile(filePath, dryRun, gitDates = new Map) {
|
|
80650
80715
|
const flags = [];
|
|
@@ -86369,6 +86434,14 @@ var init_job_exclusion_guard = __esm(() => {
|
|
|
86369
86434
|
});
|
|
86370
86435
|
|
|
86371
86436
|
// ../../packages/app/src/services/scheduler-custom-job-service.ts
|
|
86437
|
+
function isSchedulerCustomActiveConflict(error51) {
|
|
86438
|
+
if (typeof error51 !== "object" || error51 === null)
|
|
86439
|
+
return false;
|
|
86440
|
+
const { code, message } = error51;
|
|
86441
|
+
if (typeof message !== "string" || !message.includes(SCHEDULER_CUSTOM_ACTIVE_INDEX))
|
|
86442
|
+
return false;
|
|
86443
|
+
return code === undefined || String(code).startsWith("SQLITE_CONSTRAINT");
|
|
86444
|
+
}
|
|
86372
86445
|
function resolveSchedulerCustomTimeoutMs(env) {
|
|
86373
86446
|
return normalizeLegacyTimeoutMs(env.SPUR_SCHEDULER_CUSTOM_TIMEOUT_MS, SCHEDULER_CUSTOM_TIMEOUT_MS);
|
|
86374
86447
|
}
|
|
@@ -86408,7 +86481,8 @@ function validateSchedulerCustomJobPayload(raw) {
|
|
|
86408
86481
|
async function handleSchedulerCustomJob(deps, job) {
|
|
86409
86482
|
const payload = validateSchedulerCustomJobPayload(job.payload);
|
|
86410
86483
|
if (activeJobs.has(payload.name)) {
|
|
86411
|
-
|
|
86484
|
+
deps.onDuplicate?.(payload.name);
|
|
86485
|
+
return;
|
|
86412
86486
|
}
|
|
86413
86487
|
const exclusiveKey = payload.exclusiveKey;
|
|
86414
86488
|
if (exclusiveKey !== undefined) {
|
|
@@ -86444,7 +86518,7 @@ async function handleSchedulerCustomJob(deps, job) {
|
|
|
86444
86518
|
releaseExclusiveJob(exclusiveKey, `scheduler job "${payload.name}"`);
|
|
86445
86519
|
}
|
|
86446
86520
|
}
|
|
86447
|
-
var SCHEDULER_CUSTOM_JOB = "scheduler.custom", SCHEDULER_CUSTOM_TIMEOUT_MS = 600000, SCHEDULER_CUSTOM_MAX_OUTPUT = 1e6, activeJobs;
|
|
86521
|
+
var SCHEDULER_CUSTOM_JOB = "scheduler.custom", SCHEDULER_CUSTOM_ACTIVE_INDEX = "queue_jobs_scheduler_custom_active_unique", SCHEDULER_CUSTOM_TIMEOUT_MS = 600000, SCHEDULER_CUSTOM_MAX_OUTPUT = 1e6, activeJobs;
|
|
86448
86522
|
var init_scheduler_custom_job_service = __esm(() => {
|
|
86449
86523
|
init_execution_policy();
|
|
86450
86524
|
init_job_exclusion_guard();
|
|
@@ -86854,8 +86928,8 @@ import {
|
|
|
86854
86928
|
unlinkSync as unlinkSync2,
|
|
86855
86929
|
writeFileSync as writeFileSync5
|
|
86856
86930
|
} from "fs";
|
|
86857
|
-
import { homedir as
|
|
86858
|
-
import { dirname as dirname14, isAbsolute as isAbsolute3, join as
|
|
86931
|
+
import { homedir as homedir7 } from "os";
|
|
86932
|
+
import { dirname as dirname14, isAbsolute as isAbsolute3, join as join20, resolve as resolve6, sep } from "path";
|
|
86859
86933
|
function parseImporterVersion(version3) {
|
|
86860
86934
|
if (version3 === undefined) {
|
|
86861
86935
|
return null;
|
|
@@ -86910,7 +86984,7 @@ async function buildRefreshCoverage(db2, selector, fanOut) {
|
|
|
86910
86984
|
async function runSessionAugmentedRoots(source, home, cwd, dao2) {
|
|
86911
86985
|
const roots = getSourceDefinition(source).defaultRoots.map((root) => resolve6(home, root));
|
|
86912
86986
|
const runRoots = [];
|
|
86913
|
-
const runRoot =
|
|
86987
|
+
const runRoot = join20(cwd, ".spur", "run");
|
|
86914
86988
|
if (!existsSync6(runRoot))
|
|
86915
86989
|
return { roots, runRoots };
|
|
86916
86990
|
const sourcesByRun = new Map;
|
|
@@ -86920,7 +86994,7 @@ async function runSessionAugmentedRoots(source, home, cwd, dao2) {
|
|
|
86920
86994
|
sourcesByRun.set(row.run_id, sources);
|
|
86921
86995
|
}
|
|
86922
86996
|
for (const runId of readdirSync6(runRoot)) {
|
|
86923
|
-
const sessionsRoot =
|
|
86997
|
+
const sessionsRoot = join20(runRoot, runId, "agent-sessions");
|
|
86924
86998
|
if (!existsSync6(sessionsRoot))
|
|
86925
86999
|
continue;
|
|
86926
87000
|
const mappedSources = sourcesByRun.get(runId);
|
|
@@ -86929,7 +87003,7 @@ async function runSessionAugmentedRoots(source, home, cwd, dao2) {
|
|
|
86929
87003
|
const soleMappedSource = mappedSources?.size === 1 && mappedSources.has(source);
|
|
86930
87004
|
if (!directMatch && !soleMappedSource)
|
|
86931
87005
|
continue;
|
|
86932
|
-
const root = realpathSync4(
|
|
87006
|
+
const root = realpathSync4(join20(sessionsRoot, agent));
|
|
86933
87007
|
roots.push(root);
|
|
86934
87008
|
runRoots.push({ runId, root });
|
|
86935
87009
|
}
|
|
@@ -86950,7 +87024,7 @@ class HistoryService {
|
|
|
86950
87024
|
const startedAt = new Date().toISOString();
|
|
86951
87025
|
const db2 = await this.ctx.getDb();
|
|
86952
87026
|
const dao2 = new RunSessionDao(db2);
|
|
86953
|
-
const discovery = parsedSource !== "opencode" && opts.file === undefined && opts.root === undefined ? await runSessionAugmentedRoots(parsedSource, this.ctx.historyHome ??
|
|
87027
|
+
const discovery = parsedSource !== "opencode" && opts.file === undefined && opts.root === undefined ? await runSessionAugmentedRoots(parsedSource, this.ctx.historyHome ?? homedir7(), this.ctx.cwd ?? process.cwd(), dao2) : undefined;
|
|
86954
87028
|
const result = parsedSource === "opencode" && opts.file === undefined && opts.root === undefined ? await runOpenCodeImport({
|
|
86955
87029
|
db: db2,
|
|
86956
87030
|
sourceDatabase: this.ctx.openCodeSourceDatabase,
|
|
@@ -87017,7 +87091,7 @@ class HistoryService {
|
|
|
87017
87091
|
}
|
|
87018
87092
|
async maintain(opts = {}) {
|
|
87019
87093
|
const db2 = await this.ctx.getDb();
|
|
87020
|
-
const dbPath = this.ctx.cwd ?
|
|
87094
|
+
const dbPath = this.ctx.cwd ? join20(this.ctx.cwd, ".spur", "spur.db") : undefined;
|
|
87021
87095
|
return maintainDatabase(db2, {
|
|
87022
87096
|
...opts,
|
|
87023
87097
|
...dbPath && opts.dbPath === undefined ? { dbPath } : {}
|
|
@@ -87525,9 +87599,9 @@ function resolveArtifactPaths(artifact, opts) {
|
|
|
87525
87599
|
return { artifactPath: out, sidecarPath: `${out.replace(/\.json$/, "")}.errors.jsonl` };
|
|
87526
87600
|
}
|
|
87527
87601
|
const base2 = resolve6(opts.cwd, ".spur", "reports", "history");
|
|
87528
|
-
const dateDir =
|
|
87602
|
+
const dateDir = join20(base2, artifact.generatedAt.slice(0, 10));
|
|
87529
87603
|
const name = `analyze-${selectorDigest(artifact.selector)}`;
|
|
87530
|
-
return { artifactPath:
|
|
87604
|
+
return { artifactPath: join20(dateDir, `${name}.json`), sidecarPath: join20(dateDir, `${name}.errors.jsonl`) };
|
|
87531
87605
|
}
|
|
87532
87606
|
function boundCoverage(coverage) {
|
|
87533
87607
|
const overflow = [];
|
|
@@ -87550,7 +87624,7 @@ function boundCoverage(coverage) {
|
|
|
87550
87624
|
}
|
|
87551
87625
|
function updateLatestPointer(opts, artifactPath) {
|
|
87552
87626
|
const base2 = resolve6(opts.cwd, ".spur", "reports", "history");
|
|
87553
|
-
const latest =
|
|
87627
|
+
const latest = join20(base2, "latest.json");
|
|
87554
87628
|
mkdirSync7(base2, { recursive: true });
|
|
87555
87629
|
if (existsSync6(latest))
|
|
87556
87630
|
unlinkSync2(latest);
|
|
@@ -87616,7 +87690,7 @@ function formatIssue(issue2) {
|
|
|
87616
87690
|
return `${issue2.sourceFile}:${issue2.sourceLine}: ${issue2.reason}`;
|
|
87617
87691
|
}
|
|
87618
87692
|
function pruneReports(cwd, retentionDays = REPORT_RETENTION_DAYS, now2 = new Date) {
|
|
87619
|
-
const dir =
|
|
87693
|
+
const dir = join20(cwd, ".spur", "reports", "history");
|
|
87620
87694
|
let entries;
|
|
87621
87695
|
try {
|
|
87622
87696
|
entries = readdirSync6(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
@@ -87634,7 +87708,7 @@ function pruneReports(cwd, retentionDays = REPORT_RETENTION_DAYS, now2 = new Dat
|
|
|
87634
87708
|
if (entryDate >= cutoff)
|
|
87635
87709
|
continue;
|
|
87636
87710
|
try {
|
|
87637
|
-
rmSync5(
|
|
87711
|
+
rmSync5(join20(dir, name), { recursive: true, force: true });
|
|
87638
87712
|
pruned.push(name);
|
|
87639
87713
|
} catch {}
|
|
87640
87714
|
}
|
|
@@ -87744,7 +87818,7 @@ function extractResolvedWorkflowFacts(workflow) {
|
|
|
87744
87818
|
var init_composition_baseline = () => {};
|
|
87745
87819
|
|
|
87746
87820
|
// ../../packages/app/src/workflow/workflow-resolver.ts
|
|
87747
|
-
import { basename as basename8, isAbsolute as isAbsolute4, join as
|
|
87821
|
+
import { basename as basename8, isAbsolute as isAbsolute4, join as join21, resolve as resolve7 } from "path";
|
|
87748
87822
|
function registeredWorkflowPaths(config4) {
|
|
87749
87823
|
const paths = config4?.workflows?.paths ?? [".spur/workflows/"];
|
|
87750
87824
|
const bundledRoot = bundledConfigRoot();
|
|
@@ -87752,7 +87826,7 @@ function registeredWorkflowPaths(config4) {
|
|
|
87752
87826
|
for (const path9 of paths) {
|
|
87753
87827
|
if (path9.startsWith(BUNDLED_PATH_PREFIX)) {
|
|
87754
87828
|
if (bundledRoot !== null)
|
|
87755
|
-
expanded.push(
|
|
87829
|
+
expanded.push(join21(bundledRoot, path9.slice(BUNDLED_PATH_PREFIX.length)));
|
|
87756
87830
|
} else {
|
|
87757
87831
|
expanded.push(path9);
|
|
87758
87832
|
}
|
|
@@ -87764,7 +87838,7 @@ function normalizeLayerPath(cwd, path9) {
|
|
|
87764
87838
|
}
|
|
87765
87839
|
function workflowLayers(opts) {
|
|
87766
87840
|
const sharedRoot = opts.sharedRoot !== undefined ? opts.sharedRoot : bundledConfigRoot();
|
|
87767
|
-
const sharedPath = sharedRoot !== null ? normalizeLayerPath(opts.cwd,
|
|
87841
|
+
const sharedPath = sharedRoot !== null ? normalizeLayerPath(opts.cwd, join21(sharedRoot, "workflows")) : null;
|
|
87768
87842
|
const layers = [];
|
|
87769
87843
|
const seen = new Set;
|
|
87770
87844
|
const add = (id, path9) => {
|
|
@@ -87773,7 +87847,7 @@ function workflowLayers(opts) {
|
|
|
87773
87847
|
seen.add(path9);
|
|
87774
87848
|
layers.push({ id, path: path9 });
|
|
87775
87849
|
};
|
|
87776
|
-
add("project", normalizeLayerPath(opts.cwd,
|
|
87850
|
+
add("project", normalizeLayerPath(opts.cwd, join21(opts.cwd, ".spur", "workflows")));
|
|
87777
87851
|
for (const entry of opts.registered) {
|
|
87778
87852
|
if (sharedPath !== null && normalizeLayerPath(opts.cwd, entry) === sharedPath)
|
|
87779
87853
|
continue;
|
|
@@ -87817,7 +87891,7 @@ async function scanWorkflowByName(cwd, name, registered) {
|
|
|
87817
87891
|
try {
|
|
87818
87892
|
const files = (await fs3.readDir(layer.path)).filter((f) => f.endsWith(".yaml") || f.endsWith(".yml"));
|
|
87819
87893
|
for (const f of files) {
|
|
87820
|
-
const abs =
|
|
87894
|
+
const abs = join21(layer.path, f);
|
|
87821
87895
|
const wfName = await readWorkflowNameFast(fs3, abs);
|
|
87822
87896
|
if (wfName === name) {
|
|
87823
87897
|
return { path: abs, source: layer.id };
|
|
@@ -87859,16 +87933,16 @@ function resolveWorkflowFile(cwd, file2) {
|
|
|
87859
87933
|
const bundledRoot = bundledConfigRoot();
|
|
87860
87934
|
if (bundledRoot !== null) {
|
|
87861
87935
|
const sharedName = isYaml ? base2 : `${base2}.yaml`;
|
|
87862
|
-
const sharedPath =
|
|
87936
|
+
const sharedPath = join21(bundledRoot, "workflows", sharedName);
|
|
87863
87937
|
if (fs3.exists(sharedPath)) {
|
|
87864
87938
|
return { path: sharedPath, source: "shared" };
|
|
87865
87939
|
}
|
|
87866
87940
|
if (!isYaml) {
|
|
87867
|
-
const sharedPipeline =
|
|
87941
|
+
const sharedPipeline = join21(bundledRoot, "workflows", `${base2}-pipeline.yaml`);
|
|
87868
87942
|
if (fs3.exists(sharedPipeline)) {
|
|
87869
87943
|
return { path: sharedPipeline, source: "shared" };
|
|
87870
87944
|
}
|
|
87871
|
-
const sharedLiteral =
|
|
87945
|
+
const sharedLiteral = join21(bundledRoot, "workflows", base2);
|
|
87872
87946
|
if (fs3.exists(sharedLiteral)) {
|
|
87873
87947
|
return { path: sharedLiteral, source: "shared" };
|
|
87874
87948
|
}
|
|
@@ -88272,7 +88346,7 @@ var init_tripwire = __esm(() => {
|
|
|
88272
88346
|
|
|
88273
88347
|
// ../../packages/app/src/workflow/actions/agent-run.ts
|
|
88274
88348
|
import { tmpdir } from "os";
|
|
88275
|
-
import { dirname as dirname15, isAbsolute as isAbsolute5, join as
|
|
88349
|
+
import { dirname as dirname15, isAbsolute as isAbsolute5, join as join22 } from "path";
|
|
88276
88350
|
|
|
88277
88351
|
class AgentRunActionRunner {
|
|
88278
88352
|
observabilityBus;
|
|
@@ -88306,15 +88380,15 @@ class AgentRunActionRunner {
|
|
|
88306
88380
|
let sessionDir = freshSession ? undefined : asOptionalString(context4.vars.__agentSessionDir);
|
|
88307
88381
|
if (affinityOn) {
|
|
88308
88382
|
if (!sessionDir || prevAgent && prevAgent !== targetAgentDir) {
|
|
88309
|
-
sessionDir =
|
|
88383
|
+
sessionDir = join22(cwd, ".spur", "run", context4.runId, "agent-sessions", targetAgentDir);
|
|
88310
88384
|
}
|
|
88311
88385
|
} else {
|
|
88312
88386
|
if (!sessionDir) {
|
|
88313
|
-
sessionDir =
|
|
88387
|
+
sessionDir = join22(cwd, ".spur", "run", context4.runId, "agent-sessions", targetAgentDir);
|
|
88314
88388
|
}
|
|
88315
88389
|
}
|
|
88316
88390
|
if (freshSession) {
|
|
88317
|
-
sessionDir =
|
|
88391
|
+
sessionDir = join22(cwd, ".spur", "run", context4.runId, "agent-sessions", targetAgentDir, `fresh-${context4.stateOrNodeId}`);
|
|
88318
88392
|
}
|
|
88319
88393
|
const storedSessionId = freshSession ? undefined : asOptionalString(context4.vars.__agentSessionId);
|
|
88320
88394
|
let continueFlag = asOptionalBoolean(options.continue);
|
|
@@ -88406,7 +88480,7 @@ class AgentRunActionRunner {
|
|
|
88406
88480
|
flags.requiresCapabilities = JSON.stringify(requiresCapabilities.requires);
|
|
88407
88481
|
}
|
|
88408
88482
|
if (expectFile !== undefined) {
|
|
88409
|
-
const target = isAbsolute5(expectFile) ? expectFile :
|
|
88483
|
+
const target = isAbsolute5(expectFile) ? expectFile : join22(cwd, expectFile);
|
|
88410
88484
|
const fs3 = createNodeFileSystem3(cwd);
|
|
88411
88485
|
if (await fs3.exists(target)) {
|
|
88412
88486
|
try {
|
|
@@ -88425,7 +88499,7 @@ class AgentRunActionRunner {
|
|
|
88425
88499
|
return;
|
|
88426
88500
|
return `
|
|
88427
88501
|
|
|
88428
|
-
Write the required artifact to this absolute path: ${
|
|
88502
|
+
Write the required artifact to this absolute path: ${join22(cwd, rel)}`;
|
|
88429
88503
|
})();
|
|
88430
88504
|
const dispatchInput = input === undefined || absoluteArtifactHint === undefined ? input : `${input}${absoluteArtifactHint}`;
|
|
88431
88505
|
const observer = this.observabilityBus === undefined ? undefined : (event) => {
|
|
@@ -88579,13 +88653,13 @@ Write the required artifact to this absolute path: ${join21(cwd, rel)}`;
|
|
|
88579
88653
|
}
|
|
88580
88654
|
}
|
|
88581
88655
|
if (capture && answerFile !== undefined) {
|
|
88582
|
-
const target = isAbsolute5(answerFile) ? answerFile :
|
|
88656
|
+
const target = isAbsolute5(answerFile) ? answerFile : join22(cwd, answerFile);
|
|
88583
88657
|
const fs3 = createNodeFileSystem3(cwd);
|
|
88584
88658
|
await fs3.ensureDir(dirname15(target));
|
|
88585
88659
|
await fs3.writeFile(target, answer);
|
|
88586
88660
|
}
|
|
88587
88661
|
if (ok && expectFile !== undefined) {
|
|
88588
|
-
const target = isAbsolute5(expectFile) ? expectFile :
|
|
88662
|
+
const target = isAbsolute5(expectFile) ? expectFile : join22(cwd, expectFile);
|
|
88589
88663
|
const fs3 = createNodeFileSystem3(cwd);
|
|
88590
88664
|
if (!await fs3.exists(target)) {
|
|
88591
88665
|
await writePartialWorkArtifact(context4, agentLabel, model, traced, cwd, sessionDir, this.agentConfig.secretValues);
|
|
@@ -88630,7 +88704,7 @@ ${traced.stdout ?? ""}`);
|
|
|
88630
88704
|
const resolvedAgent = invocation?.agent ?? targetAgentDir;
|
|
88631
88705
|
let resolvedSessionDir = sessionDir;
|
|
88632
88706
|
if (ok && affinityOn && resolvedAgent !== targetAgentDir && !context4.vars.__agentSessionDir) {
|
|
88633
|
-
resolvedSessionDir =
|
|
88707
|
+
resolvedSessionDir = join22(cwd, ".spur", "run", context4.runId, "agent-sessions", resolvedAgent);
|
|
88634
88708
|
}
|
|
88635
88709
|
let discoveredSessionId = storedSessionId;
|
|
88636
88710
|
if (ok && affinityOn && resolvedSessionDir && !discoveredSessionId) {
|
|
@@ -88638,7 +88712,7 @@ ${traced.stdout ?? ""}`);
|
|
|
88638
88712
|
}
|
|
88639
88713
|
if (ok && affinityOn && resolvedSessionDir) {
|
|
88640
88714
|
try {
|
|
88641
|
-
const sidecarPath =
|
|
88715
|
+
const sidecarPath = join22(cwd, ".spur", "run", `${context4.runId}-agent-session.json`);
|
|
88642
88716
|
const fs3 = createNodeFileSystem3(cwd);
|
|
88643
88717
|
await fs3.ensureDir(dirname15(sidecarPath));
|
|
88644
88718
|
await fs3.writeFile(sidecarPath, JSON.stringify({
|
|
@@ -88687,7 +88761,7 @@ async function discoverSessionId(sessionDir) {
|
|
|
88687
88761
|
let newestFile;
|
|
88688
88762
|
let newestMtime = 0;
|
|
88689
88763
|
for (const entry of jsonEntries) {
|
|
88690
|
-
const fullPath =
|
|
88764
|
+
const fullPath = join22(sessionDir, entry);
|
|
88691
88765
|
const st = await fs3.stat(fullPath);
|
|
88692
88766
|
if (st?.isFile() && st.mtimeMs > newestMtime) {
|
|
88693
88767
|
newestMtime = st.mtimeMs;
|
|
@@ -88783,7 +88857,7 @@ async function writePartialWorkArtifact(context4, agentLabel, model, traced, cwd
|
|
|
88783
88857
|
const headerLine = model !== undefined ? `${agentLabel} (model: ${model})` : agentLabel;
|
|
88784
88858
|
const inv = traced.invocation;
|
|
88785
88859
|
const argvLine = redactAndBound(inv ? `${inv.command} ${inv.argv.join(" ")}` : "(invocation not captured)", secretValues, PARTIAL_ARTIFACT_TAIL_CHARS);
|
|
88786
|
-
const latchedSessionPath =
|
|
88860
|
+
const latchedSessionPath = join22(cwd, ".spur", "run", `${context4.runId}-agent-session.json`);
|
|
88787
88861
|
const resumeContext = [
|
|
88788
88862
|
"",
|
|
88789
88863
|
"## resume context",
|
|
@@ -88799,9 +88873,9 @@ async function writePartialWorkArtifact(context4, agentLabel, model, traced, cwd
|
|
|
88799
88873
|
try {
|
|
88800
88874
|
const fs4 = createNodeFileSystem3(cwd);
|
|
88801
88875
|
const locator = TaskLocator.forDirs(fs4, [
|
|
88802
|
-
|
|
88803
|
-
|
|
88804
|
-
|
|
88876
|
+
join22(cwd, "docs", "tasks3"),
|
|
88877
|
+
join22(cwd, "docs", "tasks2"),
|
|
88878
|
+
join22(cwd, "docs", "tasks")
|
|
88805
88879
|
]);
|
|
88806
88880
|
const hit = await locator.findByWbs(wbs);
|
|
88807
88881
|
if (hit) {
|
|
@@ -88857,7 +88931,7 @@ async function writePartialWorkArtifact(context4, agentLabel, model, traced, cwd
|
|
|
88857
88931
|
completedSection
|
|
88858
88932
|
].join(`
|
|
88859
88933
|
`);
|
|
88860
|
-
const target =
|
|
88934
|
+
const target = join22(cwd, ".spur", "run", `${context4.runId}-${context4.stateOrNodeId}-partial.md`);
|
|
88861
88935
|
const fs3 = createNodeFileSystem3(cwd);
|
|
88862
88936
|
await fs3.ensureDir(dirname15(target));
|
|
88863
88937
|
await fs3.writeFile(target, body);
|
|
@@ -88879,7 +88953,7 @@ async function gitDiffStat(cwd) {
|
|
|
88879
88953
|
}
|
|
88880
88954
|
}
|
|
88881
88955
|
async function createGitWorkingTreeSnapshot(cwd, excludeGlobs = ["docs/tasks3/*", "docs/features/*"]) {
|
|
88882
|
-
const indexFile =
|
|
88956
|
+
const indexFile = join22(tmpdir(), `spur-implement-scope-${crypto.randomUUID()}.index`);
|
|
88883
88957
|
const fs3 = createNodeFileSystem3(cwd);
|
|
88884
88958
|
let keepIndex = false;
|
|
88885
88959
|
try {
|
|
@@ -89010,9 +89084,9 @@ async function findOutOfScopeChanges(cwd, wbs, changed) {
|
|
|
89010
89084
|
try {
|
|
89011
89085
|
const fs3 = createNodeFileSystem3(cwd);
|
|
89012
89086
|
const locator = TaskLocator.forDirs(fs3, [
|
|
89013
|
-
|
|
89014
|
-
|
|
89015
|
-
|
|
89087
|
+
join22(cwd, "docs", "tasks3"),
|
|
89088
|
+
join22(cwd, "docs", "tasks2"),
|
|
89089
|
+
join22(cwd, "docs", "tasks")
|
|
89016
89090
|
]);
|
|
89017
89091
|
const hit = await locator.findByWbs(wbs);
|
|
89018
89092
|
if (!hit)
|
|
@@ -89062,7 +89136,7 @@ var init_child_env = __esm(() => {
|
|
|
89062
89136
|
});
|
|
89063
89137
|
|
|
89064
89138
|
// ../../packages/app/src/workflow/actions/run-path.ts
|
|
89065
|
-
import { dirname as dirname16, join as
|
|
89139
|
+
import { dirname as dirname16, join as join23, normalize, relative as relative3, resolve as resolve8, sep as sep2 } from "path";
|
|
89066
89140
|
function within(child, ancestor) {
|
|
89067
89141
|
return child === ancestor || child.startsWith(`${ancestor}${sep2}`);
|
|
89068
89142
|
}
|
|
@@ -89071,7 +89145,7 @@ async function resolveRunArtifactPath(fileSystem, workdir, pathRaw) {
|
|
|
89071
89145
|
throw new RunArtifactPathError("physical path confinement requires a FileSystem with realPath support \u2014 refusing to skip confinement (0785 R2)");
|
|
89072
89146
|
}
|
|
89073
89147
|
const lexicalTarget = normalize(resolve8(workdir, pathRaw));
|
|
89074
|
-
const lexicalRunRoot = normalize(
|
|
89148
|
+
const lexicalRunRoot = normalize(join23(resolve8(workdir), ".spur", "run"));
|
|
89075
89149
|
if (!lexicalTarget.startsWith(`${lexicalRunRoot}${sep2}`)) {
|
|
89076
89150
|
throw new RunArtifactPathError(`must resolve beneath .spur/run/ (got ${pathRaw})`);
|
|
89077
89151
|
}
|
|
@@ -89082,7 +89156,7 @@ async function resolveRunArtifactPath(fileSystem, workdir, pathRaw) {
|
|
|
89082
89156
|
} catch (error51) {
|
|
89083
89157
|
throw new RunArtifactPathError(`project workdir could not be canonicalized: ${error51.message}`);
|
|
89084
89158
|
}
|
|
89085
|
-
const canonicalRunRootCandidate =
|
|
89159
|
+
const canonicalRunRootCandidate = join23(canonicalWorkdir, ".spur", "run");
|
|
89086
89160
|
let canonicalRunRoot = canonicalRunRootCandidate;
|
|
89087
89161
|
const rootStat = await fileSystem.stat(canonicalRunRootCandidate);
|
|
89088
89162
|
if (rootStat !== null) {
|
|
@@ -89130,7 +89204,7 @@ async function resolveRunArtifactPath(fileSystem, workdir, pathRaw) {
|
|
|
89130
89204
|
if (!within(anchor, canonicalWorkdir)) {
|
|
89131
89205
|
throw new RunArtifactPathError(`ancestor ${probe} escapes the project workdir through a symlink`);
|
|
89132
89206
|
}
|
|
89133
|
-
const reconstructed = normalize(
|
|
89207
|
+
const reconstructed = normalize(join23(anchor, relative3(probe, lexicalTarget)));
|
|
89134
89208
|
if (!reconstructed.startsWith(`${canonicalRunRoot}${sep2}`)) {
|
|
89135
89209
|
throw new RunArtifactPathError(`path escapes .spur/run/ through a symlink (got ${pathRaw})`);
|
|
89136
89210
|
}
|
|
@@ -89147,7 +89221,7 @@ var init_run_path = __esm(() => {
|
|
|
89147
89221
|
});
|
|
89148
89222
|
|
|
89149
89223
|
// ../../packages/app/src/workflow/actions/command-gate.ts
|
|
89150
|
-
import { join as
|
|
89224
|
+
import { join as join24, normalize as normalize2 } from "path";
|
|
89151
89225
|
function sleep2(ms) {
|
|
89152
89226
|
return new Promise((resolve9) => setTimeout(resolve9, ms));
|
|
89153
89227
|
}
|
|
@@ -89219,7 +89293,7 @@ class CommandGateActionRunner {
|
|
|
89219
89293
|
const retryOn = Array.isArray(retry.on) ? retry.on : [];
|
|
89220
89294
|
const timeoutMs = typeof options.timeoutMs === "number" ? options.timeoutMs : undefined;
|
|
89221
89295
|
const env = childProcessEnv(context4.vars);
|
|
89222
|
-
await this.fileSystem.ensureDir(normalize2(
|
|
89296
|
+
await this.fileSystem.ensureDir(normalize2(join24(normalized, "..")));
|
|
89223
89297
|
let lastStdout = "";
|
|
89224
89298
|
let lastStderr = "";
|
|
89225
89299
|
let lastExitCode = -1;
|
|
@@ -89876,7 +89950,7 @@ var init_http_request = __esm(() => {
|
|
|
89876
89950
|
// ../../packages/app/src/workflow/proof-input-fingerprint.ts
|
|
89877
89951
|
import crypto4 from "crypto";
|
|
89878
89952
|
import { tmpdir as tmpdir2 } from "os";
|
|
89879
|
-
import { dirname as dirname17, join as
|
|
89953
|
+
import { dirname as dirname17, join as join25, resolve as resolve10, sep as sep4 } from "path";
|
|
89880
89954
|
function taskDocumentShapeError(content) {
|
|
89881
89955
|
const doc2 = MarkdownDocument.parse(content, "task");
|
|
89882
89956
|
const heading = CANONICAL_TASK_HEADING_RE.exec(doc2.bodyWithoutFrontmatter);
|
|
@@ -89951,7 +90025,7 @@ async function readProofInputContents(fileSystem = createNodeFileSystem3(), work
|
|
|
89951
90025
|
};
|
|
89952
90026
|
}
|
|
89953
90027
|
async function createGitAlternateTree(cwd, excludeGlobs = DEFAULT_EXCLUDE_GLOBS, executor = new NodeProcessExecutor3, fs3 = createNodeFileSystem3()) {
|
|
89954
|
-
const indexFile =
|
|
90028
|
+
const indexFile = join25(tmpdir2(), `spur-proof-fingerprint-${crypto4.randomUUID()}.index`);
|
|
89955
90029
|
try {
|
|
89956
90030
|
await fs3.ensureDir(dirname17(indexFile));
|
|
89957
90031
|
const env = Object.fromEntries(Object.entries(process.env).filter((entry) => entry[1] !== undefined));
|
|
@@ -90246,7 +90320,7 @@ var init_rule_check = __esm(() => {
|
|
|
90246
90320
|
});
|
|
90247
90321
|
|
|
90248
90322
|
// ../../packages/app/src/workflow/actions/run-artifact.ts
|
|
90249
|
-
import { join as
|
|
90323
|
+
import { join as join26, resolve as resolve11 } from "path";
|
|
90250
90324
|
|
|
90251
90325
|
class RunArtifactActionRunner {
|
|
90252
90326
|
getDb;
|
|
@@ -90524,7 +90598,7 @@ class RunArtifactActionRunner {
|
|
|
90524
90598
|
error: `${KIND14}: proof.stages.review.status is "${String(review.status)}" \u2014 a skipped or stale review is not completed evidence (0785 R4)`
|
|
90525
90599
|
};
|
|
90526
90600
|
}
|
|
90527
|
-
const markerPath =
|
|
90601
|
+
const markerPath = join26(resolve11(workdir), ".spur", "run", `${context4.runId}-review-proof.digest`);
|
|
90528
90602
|
let marker = null;
|
|
90529
90603
|
try {
|
|
90530
90604
|
marker = await this.fileSystem.readFile(markerPath);
|
|
@@ -90868,7 +90942,7 @@ var init_checkpoint_contract = __esm(() => {
|
|
|
90868
90942
|
|
|
90869
90943
|
// ../../packages/app/src/services/workflow-service.ts
|
|
90870
90944
|
import { realpathSync as realpathSync5 } from "fs";
|
|
90871
|
-
import { basename as basename9, dirname as dirname18, isAbsolute as isAbsolute6, join as
|
|
90945
|
+
import { basename as basename9, dirname as dirname18, isAbsolute as isAbsolute6, join as join27, resolve as resolve12, sep as sep5 } from "path";
|
|
90872
90946
|
function signalSubprocess(pid) {
|
|
90873
90947
|
if (!Number.isInteger(pid) || pid <= 1)
|
|
90874
90948
|
return false;
|
|
@@ -91133,7 +91207,7 @@ class WorkflowAppService {
|
|
|
91133
91207
|
};
|
|
91134
91208
|
}
|
|
91135
91209
|
async cleanRunLogs(retentionDays = 30, dryRun = false) {
|
|
91136
|
-
const runDir =
|
|
91210
|
+
const runDir = join27(this.ctx.cwd, ".spur", "run");
|
|
91137
91211
|
const fs3 = createNodeFileSystem3();
|
|
91138
91212
|
const cutoffMs = Date.now() - retentionDays * 24 * 60 * 60 * 1000;
|
|
91139
91213
|
const reclaimed = [];
|
|
@@ -91145,7 +91219,7 @@ class WorkflowAppService {
|
|
|
91145
91219
|
return { retentionDays, dryRun, reclaimed, failures };
|
|
91146
91220
|
}
|
|
91147
91221
|
for (const name of entries) {
|
|
91148
|
-
const path9 =
|
|
91222
|
+
const path9 = join27(runDir, name);
|
|
91149
91223
|
const stat2 = await fs3.stat(path9);
|
|
91150
91224
|
if (stat2 === null || !stat2.isFile() || stat2.mtimeMs >= cutoffMs)
|
|
91151
91225
|
continue;
|
|
@@ -91164,7 +91238,7 @@ class WorkflowAppService {
|
|
|
91164
91238
|
return { retentionDays, dryRun, reclaimed, failures };
|
|
91165
91239
|
}
|
|
91166
91240
|
async cleanCheckpoints(retentionDays = 30, dryRun = false) {
|
|
91167
|
-
const sessionsDir =
|
|
91241
|
+
const sessionsDir = join27(this.ctx.cwd, ".spur", "memory", "sessions");
|
|
91168
91242
|
const fs3 = createNodeFileSystem3();
|
|
91169
91243
|
let sessionsReal;
|
|
91170
91244
|
try {
|
|
@@ -91184,7 +91258,7 @@ class WorkflowAppService {
|
|
|
91184
91258
|
}
|
|
91185
91259
|
const activeRunIds = new Set((await new RunDao(await this.ctx.getDb()).listActiveRuns()).map((r) => r.id));
|
|
91186
91260
|
for (const name of entries) {
|
|
91187
|
-
const path9 =
|
|
91261
|
+
const path9 = join27(sessionsDir, name);
|
|
91188
91262
|
let resolved;
|
|
91189
91263
|
try {
|
|
91190
91264
|
resolved = realpathSync5(path9);
|
|
@@ -91384,7 +91458,7 @@ class WorkflowAppService {
|
|
|
91384
91458
|
await new RunDao(db2).stampFailureReason(runId, result.reason);
|
|
91385
91459
|
}
|
|
91386
91460
|
async validateResumeCheckpointFreshness(runId, row, workdir) {
|
|
91387
|
-
const sessionsDir =
|
|
91461
|
+
const sessionsDir = join27(workdir, ".spur", "memory", "sessions");
|
|
91388
91462
|
const fs3 = createNodeFileSystem3();
|
|
91389
91463
|
let entries;
|
|
91390
91464
|
try {
|
|
@@ -91418,7 +91492,7 @@ class WorkflowAppService {
|
|
|
91418
91492
|
return head;
|
|
91419
91493
|
};
|
|
91420
91494
|
for (const name of entries) {
|
|
91421
|
-
const filePath =
|
|
91495
|
+
const filePath = join27(sessionsDir, name);
|
|
91422
91496
|
let raw;
|
|
91423
91497
|
try {
|
|
91424
91498
|
raw = await fs3.readFile(filePath);
|
|
@@ -91449,7 +91523,7 @@ class WorkflowAppService {
|
|
|
91449
91523
|
const staleness = checkpointStaleness(meta3, {
|
|
91450
91524
|
...wbs !== undefined ? { taskWbs: wbs } : {},
|
|
91451
91525
|
...meta3.sourceCommit === "" ? {} : { sourceCommit: await resolveHead() },
|
|
91452
|
-
artifactExists: (p) => fs3.exists(isAbsolute6(p) ? p :
|
|
91526
|
+
artifactExists: (p) => fs3.exists(isAbsolute6(p) ? p : join27(workdir, p))
|
|
91453
91527
|
});
|
|
91454
91528
|
if (staleness.stale) {
|
|
91455
91529
|
throw new Error(`Cannot resume run "${runId}": checkpoint is stale (${staleness.reason}).`);
|
|
@@ -91994,8 +92068,8 @@ function resolveOutputLogConfig(config4) {
|
|
|
91994
92068
|
};
|
|
91995
92069
|
}
|
|
91996
92070
|
async function outputArtifactForRun(cwd, runId) {
|
|
91997
|
-
const relative4 =
|
|
91998
|
-
return await fileExists(
|
|
92071
|
+
const relative4 = join27(".spur", "run", `${runId}.log`);
|
|
92072
|
+
return await fileExists(join27(cwd, relative4)) ? relative4 : undefined;
|
|
91999
92073
|
}
|
|
92000
92074
|
async function scanWorkflowFiles(rootPath, source, loadEntry) {
|
|
92001
92075
|
const entries = [];
|
|
@@ -92193,7 +92267,7 @@ function projectActionTraceResult(resultJson, secretValues = []) {
|
|
|
92193
92267
|
async function partialArtifactForAction(cwd, runId, node, ok) {
|
|
92194
92268
|
if (ok !== false || !TRACE_IDENTIFIER.test(runId) || !TRACE_IDENTIFIER.test(node))
|
|
92195
92269
|
return;
|
|
92196
|
-
const relativePath2 =
|
|
92270
|
+
const relativePath2 = join27(".spur", "run", `${runId}-${node}-partial.md`);
|
|
92197
92271
|
return await fileExists(resolve12(cwd, relativePath2)) ? relativePath2 : undefined;
|
|
92198
92272
|
}
|
|
92199
92273
|
var TASK_PIPELINE_WORKFLOW = "task-pipeline", PIPELINE_LINK_KIND = "pipeline", COMPOSITION_CAPS, STRUCTURE_TOKENS, TRACE_IDENTIFIER, TRACE_RESULT_FIELDS, TRACE_INVOCATION_FIELDS;
|
|
@@ -92238,7 +92312,7 @@ var init_workflow_service = __esm(() => {
|
|
|
92238
92312
|
});
|
|
92239
92313
|
|
|
92240
92314
|
// ../../packages/app/src/services/inline-run-setup.ts
|
|
92241
|
-
import { join as
|
|
92315
|
+
import { join as join28, resolve as resolve13 } from "path";
|
|
92242
92316
|
function parseIdentityMetadata(raw) {
|
|
92243
92317
|
if (raw.trim() === "")
|
|
92244
92318
|
return {};
|
|
@@ -92252,8 +92326,8 @@ function parseIdentityMetadata(raw) {
|
|
|
92252
92326
|
}
|
|
92253
92327
|
}
|
|
92254
92328
|
async function openInlineRunProjectDb(workdir) {
|
|
92255
|
-
const url2 =
|
|
92256
|
-
mkdirSync8(
|
|
92329
|
+
const url2 = join28(resolve13(workdir), ".spur", "spur.db");
|
|
92330
|
+
mkdirSync8(join28(url2, ".."), { recursive: true });
|
|
92257
92331
|
const adapter = await createMigratedDb({ url: url2 });
|
|
92258
92332
|
return { adapter, close: () => adapter.close() };
|
|
92259
92333
|
}
|
|
@@ -93205,8 +93279,8 @@ var init_project_start = __esm(() => {
|
|
|
93205
93279
|
});
|
|
93206
93280
|
|
|
93207
93281
|
// ../../packages/app/src/services/rule-service.ts
|
|
93208
|
-
import { homedir as
|
|
93209
|
-
import { delimiter, join as
|
|
93282
|
+
import { homedir as homedir8 } from "os";
|
|
93283
|
+
import { delimiter, join as join29, relative as relative4, resolve as resolve15 } from "path";
|
|
93210
93284
|
|
|
93211
93285
|
class RuleService {
|
|
93212
93286
|
context;
|
|
@@ -93401,13 +93475,13 @@ ${ruleIds.join(`
|
|
|
93401
93475
|
const hasGlobalOverride = globalOverride !== undefined && globalOverride.length > 0;
|
|
93402
93476
|
layers.push({
|
|
93403
93477
|
id: "global",
|
|
93404
|
-
path: hasGlobalOverride ? resolve15(cwd, globalOverride) :
|
|
93478
|
+
path: hasGlobalOverride ? resolve15(cwd, globalOverride) : join29(homedir8(), GLOBAL_RULES_DIR),
|
|
93405
93479
|
priority: 10
|
|
93406
93480
|
});
|
|
93407
93481
|
if (opts.includeBundled && !hasGlobalOverride) {
|
|
93408
93482
|
const bundledConfig = bundledConfigRoot();
|
|
93409
93483
|
if (bundledConfig !== null) {
|
|
93410
|
-
layers.push({ id: "bundled-config", path:
|
|
93484
|
+
layers.push({ id: "bundled-config", path: join29(bundledConfig, "rules"), priority: 15 });
|
|
93411
93485
|
}
|
|
93412
93486
|
const bundled = await bundledRulesRoot();
|
|
93413
93487
|
if (bundled !== null)
|
|
@@ -93620,7 +93694,7 @@ ${ruleIds.join(`
|
|
|
93620
93694
|
const { fs: fs3 } = this.context;
|
|
93621
93695
|
for (const root of await this.ruleRoots()) {
|
|
93622
93696
|
for (const ext of ["yaml", "yml", "json"]) {
|
|
93623
|
-
if (await fs3.exists(
|
|
93697
|
+
if (await fs3.exists(join29(root, `${name}.${ext}`)))
|
|
93624
93698
|
return true;
|
|
93625
93699
|
}
|
|
93626
93700
|
}
|
|
@@ -93656,7 +93730,7 @@ ${ruleIds.join(`
|
|
|
93656
93730
|
for (const layer of await this.existingRuleSourceLayers()) {
|
|
93657
93731
|
const entries = await this.context.fs.readDir(layer.path);
|
|
93658
93732
|
const named = entries.map((entry) => ({ entry, name: entry.match(/^([\w-]+)\.(?:ya?ml|json)$/i)?.[1] ?? "" })).filter(({ name }) => name.length > 0);
|
|
93659
|
-
const stats = await Promise.all(named.map(({ entry }) => this.context.fs.stat(
|
|
93733
|
+
const stats = await Promise.all(named.map(({ entry }) => this.context.fs.stat(join29(layer.path, entry))));
|
|
93660
93734
|
named.forEach(({ name }, i2) => {
|
|
93661
93735
|
if (stats[i2]?.isFile())
|
|
93662
93736
|
candidates.add(name);
|
|
@@ -93678,7 +93752,7 @@ ${ruleIds.join(`
|
|
|
93678
93752
|
for (const layer of layers) {
|
|
93679
93753
|
for (const file2 of await this.listRuleFilesInLayer(layer.path)) {
|
|
93680
93754
|
if (!merged.has(file2))
|
|
93681
|
-
merged.set(file2, { absolutePath:
|
|
93755
|
+
merged.set(file2, { absolutePath: join29(layer.path, file2), source: layer.id });
|
|
93682
93756
|
}
|
|
93683
93757
|
}
|
|
93684
93758
|
const files = [];
|
|
@@ -93746,7 +93820,7 @@ ${ruleIds.join(`
|
|
|
93746
93820
|
for (const entry of entries.sort()) {
|
|
93747
93821
|
if (entry === "presets")
|
|
93748
93822
|
continue;
|
|
93749
|
-
const stat2 = await this.context.fs.stat(
|
|
93823
|
+
const stat2 = await this.context.fs.stat(join29(root, entry));
|
|
93750
93824
|
if (stat2?.isDirectory())
|
|
93751
93825
|
dirs.push(entry);
|
|
93752
93826
|
}
|
|
@@ -93754,12 +93828,12 @@ ${ruleIds.join(`
|
|
|
93754
93828
|
}
|
|
93755
93829
|
async listRuleFiles(root, relativeDir) {
|
|
93756
93830
|
const { fs: fs3 } = this.context;
|
|
93757
|
-
const dir =
|
|
93831
|
+
const dir = join29(root, relativeDir);
|
|
93758
93832
|
const entries = await fs3.readDir(dir);
|
|
93759
93833
|
const files = [];
|
|
93760
93834
|
for (const entry of entries.sort()) {
|
|
93761
|
-
const relativePath2 = relativeDir.length === 0 ? entry :
|
|
93762
|
-
const absolutePath =
|
|
93835
|
+
const relativePath2 = relativeDir.length === 0 ? entry : join29(relativeDir, entry);
|
|
93836
|
+
const absolutePath = join29(root, relativePath2);
|
|
93763
93837
|
const stat2 = await fs3.stat(absolutePath);
|
|
93764
93838
|
if (stat2?.isDirectory()) {
|
|
93765
93839
|
files.push(...await this.listRuleFiles(root, relativePath2));
|
|
@@ -93893,8 +93967,8 @@ var init_rule_service = __esm(() => {
|
|
|
93893
93967
|
init_dist10();
|
|
93894
93968
|
init_agent_execution();
|
|
93895
93969
|
init_system_event_envelope();
|
|
93896
|
-
LOCAL_RULES_DIR =
|
|
93897
|
-
GLOBAL_RULES_DIR =
|
|
93970
|
+
LOCAL_RULES_DIR = join29(".spur", "rules");
|
|
93971
|
+
GLOBAL_RULES_DIR = join29(".config", "spur", "rules");
|
|
93898
93972
|
SEVERITY_RANK2 = {
|
|
93899
93973
|
info: 0,
|
|
93900
93974
|
warning: 1,
|
|
@@ -94109,6 +94183,368 @@ var init_run_store_service = __esm(() => {
|
|
|
94109
94183
|
};
|
|
94110
94184
|
});
|
|
94111
94185
|
|
|
94186
|
+
// ../../packages/app/src/services/slash-commands-service.ts
|
|
94187
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync9, readdirSync as readdirSync7, readFileSync as readFileSync10, writeFileSync as writeFileSync6 } from "fs";
|
|
94188
|
+
import { homedir as homedir9 } from "os";
|
|
94189
|
+
import { basename as basename11, dirname as dirname19, join as join30, resolve as resolve16 } from "path";
|
|
94190
|
+
function getBuiltinClaudeCommands() {
|
|
94191
|
+
return BUILTIN_CLAUDE_COMMANDS.map((cmd) => ({ ...cmd }));
|
|
94192
|
+
}
|
|
94193
|
+
function deduceCommandCategory(nameOrStem) {
|
|
94194
|
+
const lower = nameOrStem.toLowerCase();
|
|
94195
|
+
if (/(git|commit|review|revert|pr-review|diff|branch|stash)/.test(lower)) {
|
|
94196
|
+
return "git";
|
|
94197
|
+
}
|
|
94198
|
+
if (/(rule|workflow|init|harness|spur|plan|run|verify|wrap|refine|parallel|idea)/.test(lower)) {
|
|
94199
|
+
return "harness";
|
|
94200
|
+
}
|
|
94201
|
+
if (/(doctor|cost|compact|clear|terminal|\bbug\b|config|login|logout|sys|status|ps)/.test(lower)) {
|
|
94202
|
+
return "sys";
|
|
94203
|
+
}
|
|
94204
|
+
return "dev";
|
|
94205
|
+
}
|
|
94206
|
+
function findPluginsSpCommandsDir(startDir = process.cwd()) {
|
|
94207
|
+
let current = resolve16(startDir);
|
|
94208
|
+
while (true) {
|
|
94209
|
+
try {
|
|
94210
|
+
const candidate = join30(current, "plugins", "sp", "commands");
|
|
94211
|
+
if (existsSync8(candidate)) {
|
|
94212
|
+
return candidate;
|
|
94213
|
+
}
|
|
94214
|
+
} catch {
|
|
94215
|
+
break;
|
|
94216
|
+
}
|
|
94217
|
+
const parent = dirname19(current);
|
|
94218
|
+
if (parent === current) {
|
|
94219
|
+
break;
|
|
94220
|
+
}
|
|
94221
|
+
current = parent;
|
|
94222
|
+
}
|
|
94223
|
+
return null;
|
|
94224
|
+
}
|
|
94225
|
+
function findProjectClaudeCommandsDir(startDir = process.cwd()) {
|
|
94226
|
+
let current = resolve16(startDir);
|
|
94227
|
+
while (true) {
|
|
94228
|
+
try {
|
|
94229
|
+
const candidate = join30(current, ".claude", "commands");
|
|
94230
|
+
if (existsSync8(candidate)) {
|
|
94231
|
+
return candidate;
|
|
94232
|
+
}
|
|
94233
|
+
} catch {
|
|
94234
|
+
break;
|
|
94235
|
+
}
|
|
94236
|
+
const parent = dirname19(current);
|
|
94237
|
+
if (parent === current) {
|
|
94238
|
+
break;
|
|
94239
|
+
}
|
|
94240
|
+
current = parent;
|
|
94241
|
+
}
|
|
94242
|
+
return null;
|
|
94243
|
+
}
|
|
94244
|
+
function parseMarkdownFrontmatter(rawContent) {
|
|
94245
|
+
const trimmed = rawContent.trimStart();
|
|
94246
|
+
if (!trimmed.startsWith("---")) {
|
|
94247
|
+
return { frontmatter: {}, body: rawContent };
|
|
94248
|
+
}
|
|
94249
|
+
const match = trimmed.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);
|
|
94250
|
+
if (!match) {
|
|
94251
|
+
return { frontmatter: {}, body: rawContent };
|
|
94252
|
+
}
|
|
94253
|
+
try {
|
|
94254
|
+
const parsed = $parse(match[1] ?? "");
|
|
94255
|
+
return {
|
|
94256
|
+
frontmatter: typeof parsed === "object" && parsed !== null ? parsed : {},
|
|
94257
|
+
body: match[2] ?? ""
|
|
94258
|
+
};
|
|
94259
|
+
} catch {
|
|
94260
|
+
return { frontmatter: {}, body: rawContent };
|
|
94261
|
+
}
|
|
94262
|
+
}
|
|
94263
|
+
function scanMarkdownCommands(dirPath, options = {}) {
|
|
94264
|
+
if (!existsSync8(dirPath)) {
|
|
94265
|
+
return [];
|
|
94266
|
+
}
|
|
94267
|
+
const { prefix = "/", source = "plugin:sp", defaultCategory } = options;
|
|
94268
|
+
const items = [];
|
|
94269
|
+
try {
|
|
94270
|
+
const entries = readdirSync7(dirPath, { withFileTypes: true });
|
|
94271
|
+
for (const entry of entries) {
|
|
94272
|
+
if (!entry.isFile() || !entry.name.endsWith(".md")) {
|
|
94273
|
+
continue;
|
|
94274
|
+
}
|
|
94275
|
+
const fullPath = join30(dirPath, entry.name);
|
|
94276
|
+
const stem = basename11(entry.name, ".md");
|
|
94277
|
+
const fileContent = readFileSync10(fullPath, "utf-8");
|
|
94278
|
+
const { frontmatter, body } = parseMarkdownFrontmatter(fileContent);
|
|
94279
|
+
const cleanPrefix = prefix.endsWith(":") ? prefix : prefix.endsWith("/") ? prefix : `${prefix}:`;
|
|
94280
|
+
const name = prefix === "/" ? `/${stem}` : `${cleanPrefix}${stem}`;
|
|
94281
|
+
let description = "";
|
|
94282
|
+
if (typeof frontmatter.description === "string" && frontmatter.description.trim().length > 0) {
|
|
94283
|
+
description = frontmatter.description.trim();
|
|
94284
|
+
} else {
|
|
94285
|
+
const lines = body.split(`
|
|
94286
|
+
`).map((l) => l.trim()).filter((l) => l.length > 0);
|
|
94287
|
+
const firstHeading = lines.find((l) => l.startsWith("# "));
|
|
94288
|
+
const firstLine = lines[0];
|
|
94289
|
+
if (firstHeading) {
|
|
94290
|
+
description = firstHeading.replace(/^#\s+/, "").trim();
|
|
94291
|
+
} else if (firstLine !== undefined) {
|
|
94292
|
+
description = firstLine.replace(/^[#*-\s]+/, "").trim();
|
|
94293
|
+
} else {
|
|
94294
|
+
description = stem.replace(/[-_]/g, " ");
|
|
94295
|
+
}
|
|
94296
|
+
}
|
|
94297
|
+
let category;
|
|
94298
|
+
const fmCategory = typeof frontmatter.category === "string" ? frontmatter.category.toLowerCase() : "";
|
|
94299
|
+
if (fmCategory === "git" || fmCategory === "dev" || fmCategory === "sys" || fmCategory === "harness") {
|
|
94300
|
+
category = fmCategory;
|
|
94301
|
+
} else {
|
|
94302
|
+
category = defaultCategory ?? deduceCommandCategory(stem);
|
|
94303
|
+
}
|
|
94304
|
+
const hintRaw = frontmatter["argument-hint"] ?? frontmatter.argumentHint;
|
|
94305
|
+
const argumentHint = typeof hintRaw === "string" && hintRaw.trim().length > 0 ? hintRaw.trim() : undefined;
|
|
94306
|
+
const roleRaw = frontmatter.role;
|
|
94307
|
+
const role = typeof roleRaw === "string" && roleRaw.trim().length > 0 ? roleRaw.trim() : undefined;
|
|
94308
|
+
items.push({
|
|
94309
|
+
name,
|
|
94310
|
+
description,
|
|
94311
|
+
category,
|
|
94312
|
+
argumentHint,
|
|
94313
|
+
role,
|
|
94314
|
+
source
|
|
94315
|
+
});
|
|
94316
|
+
}
|
|
94317
|
+
} catch {}
|
|
94318
|
+
return items;
|
|
94319
|
+
}
|
|
94320
|
+
function generateSlashCommands(options = {}) {
|
|
94321
|
+
const {
|
|
94322
|
+
projectRoot = process.cwd(),
|
|
94323
|
+
includeBuiltin = true,
|
|
94324
|
+
includePluginSp = true,
|
|
94325
|
+
includeProjectClaude = true,
|
|
94326
|
+
includeUserClaude = true,
|
|
94327
|
+
includeUserSpur = true
|
|
94328
|
+
} = options;
|
|
94329
|
+
const commandMap = new Map;
|
|
94330
|
+
if (includeBuiltin) {
|
|
94331
|
+
for (const cmd of getBuiltinClaudeCommands()) {
|
|
94332
|
+
commandMap.set(cmd.name, cmd);
|
|
94333
|
+
}
|
|
94334
|
+
}
|
|
94335
|
+
if (includePluginSp) {
|
|
94336
|
+
const spCommandsDir = findPluginsSpCommandsDir(projectRoot) ?? resolve16(projectRoot, "plugins", "sp", "commands");
|
|
94337
|
+
const spCommands = scanMarkdownCommands(spCommandsDir, {
|
|
94338
|
+
prefix: "/sp:",
|
|
94339
|
+
source: "plugin:sp",
|
|
94340
|
+
defaultCategory: "harness"
|
|
94341
|
+
});
|
|
94342
|
+
for (const cmd of spCommands) {
|
|
94343
|
+
commandMap.set(cmd.name, cmd);
|
|
94344
|
+
}
|
|
94345
|
+
}
|
|
94346
|
+
if (includeProjectClaude) {
|
|
94347
|
+
const projectClaudeDir = findProjectClaudeCommandsDir(projectRoot) ?? resolve16(projectRoot, ".claude", "commands");
|
|
94348
|
+
const projectCommands = scanMarkdownCommands(projectClaudeDir, {
|
|
94349
|
+
prefix: "/",
|
|
94350
|
+
source: "claude:project"
|
|
94351
|
+
});
|
|
94352
|
+
for (const cmd of projectCommands) {
|
|
94353
|
+
commandMap.set(cmd.name, cmd);
|
|
94354
|
+
}
|
|
94355
|
+
}
|
|
94356
|
+
if (includeUserClaude) {
|
|
94357
|
+
const userClaudeDir = join30(homedir9(), ".claude", "commands");
|
|
94358
|
+
const userClaudeCommands = scanMarkdownCommands(userClaudeDir, {
|
|
94359
|
+
prefix: "/",
|
|
94360
|
+
source: "claude:user"
|
|
94361
|
+
});
|
|
94362
|
+
for (const cmd of userClaudeCommands) {
|
|
94363
|
+
commandMap.set(cmd.name, cmd);
|
|
94364
|
+
}
|
|
94365
|
+
}
|
|
94366
|
+
if (includeUserSpur) {
|
|
94367
|
+
const userSpurDir = join30(homedir9(), ".config", "spur", "commands");
|
|
94368
|
+
const userSpurCommands = scanMarkdownCommands(userSpurDir, {
|
|
94369
|
+
prefix: "/sp:",
|
|
94370
|
+
source: "spur:user",
|
|
94371
|
+
defaultCategory: "harness"
|
|
94372
|
+
});
|
|
94373
|
+
for (const cmd of userSpurCommands) {
|
|
94374
|
+
commandMap.set(cmd.name, cmd);
|
|
94375
|
+
}
|
|
94376
|
+
}
|
|
94377
|
+
return Array.from(commandMap.values());
|
|
94378
|
+
}
|
|
94379
|
+
function generateSlashCommandsFile(targetPath = getSlashCommandsFilePath(), options = {}) {
|
|
94380
|
+
const commands = generateSlashCommands(options);
|
|
94381
|
+
const filePayload = {
|
|
94382
|
+
version: 1,
|
|
94383
|
+
updatedAt: new Date().toISOString(),
|
|
94384
|
+
commands
|
|
94385
|
+
};
|
|
94386
|
+
try {
|
|
94387
|
+
const dir = dirname19(targetPath);
|
|
94388
|
+
if (!existsSync8(dir)) {
|
|
94389
|
+
mkdirSync9(dir, { recursive: true });
|
|
94390
|
+
}
|
|
94391
|
+
writeFileSync6(targetPath, JSON.stringify(filePayload, null, 2), "utf-8");
|
|
94392
|
+
} catch {}
|
|
94393
|
+
return {
|
|
94394
|
+
path: targetPath,
|
|
94395
|
+
count: commands.length,
|
|
94396
|
+
commands
|
|
94397
|
+
};
|
|
94398
|
+
}
|
|
94399
|
+
function loadSlashCommands(filePath = getSlashCommandsFilePath(), autoGenerateIfMissing = true, options = {}) {
|
|
94400
|
+
if (!existsSync8(filePath)) {
|
|
94401
|
+
if (autoGenerateIfMissing) {
|
|
94402
|
+
const generated = generateSlashCommandsFile(filePath, options);
|
|
94403
|
+
return generated.commands;
|
|
94404
|
+
}
|
|
94405
|
+
return getBuiltinClaudeCommands();
|
|
94406
|
+
}
|
|
94407
|
+
try {
|
|
94408
|
+
const content = readFileSync10(filePath, "utf-8");
|
|
94409
|
+
const raw = JSON.parse(content);
|
|
94410
|
+
const parsed = slashCommandsFileSchema.safeParse(raw);
|
|
94411
|
+
if (parsed.success && parsed.data.commands.length > 0) {
|
|
94412
|
+
return parsed.data.commands;
|
|
94413
|
+
}
|
|
94414
|
+
} catch {}
|
|
94415
|
+
if (autoGenerateIfMissing) {
|
|
94416
|
+
const generated = generateSlashCommandsFile(filePath, options);
|
|
94417
|
+
return generated.commands;
|
|
94418
|
+
}
|
|
94419
|
+
return getBuiltinClaudeCommands();
|
|
94420
|
+
}
|
|
94421
|
+
|
|
94422
|
+
class SlashCommandsService {
|
|
94423
|
+
filePath;
|
|
94424
|
+
projectRoot;
|
|
94425
|
+
cachedCommands = null;
|
|
94426
|
+
constructor(options = {}) {
|
|
94427
|
+
this.filePath = options.filePath ?? getSlashCommandsFilePath();
|
|
94428
|
+
this.projectRoot = options.projectRoot ?? process.cwd();
|
|
94429
|
+
}
|
|
94430
|
+
getFilePath() {
|
|
94431
|
+
return this.filePath;
|
|
94432
|
+
}
|
|
94433
|
+
getCommands(forceReload = false) {
|
|
94434
|
+
if (!this.cachedCommands || forceReload) {
|
|
94435
|
+
this.cachedCommands = loadSlashCommands(this.filePath, true, {
|
|
94436
|
+
projectRoot: this.projectRoot
|
|
94437
|
+
});
|
|
94438
|
+
}
|
|
94439
|
+
return this.cachedCommands;
|
|
94440
|
+
}
|
|
94441
|
+
generate(targetPath) {
|
|
94442
|
+
const dest = targetPath ?? this.filePath;
|
|
94443
|
+
const result = generateSlashCommandsFile(dest, {
|
|
94444
|
+
projectRoot: this.projectRoot
|
|
94445
|
+
});
|
|
94446
|
+
this.cachedCommands = result.commands;
|
|
94447
|
+
return result;
|
|
94448
|
+
}
|
|
94449
|
+
}
|
|
94450
|
+
var BUILTIN_CLAUDE_COMMANDS;
|
|
94451
|
+
var init_slash_commands_service = __esm(() => {
|
|
94452
|
+
init_src();
|
|
94453
|
+
init_dist2();
|
|
94454
|
+
BUILTIN_CLAUDE_COMMANDS = [
|
|
94455
|
+
{
|
|
94456
|
+
name: "/help",
|
|
94457
|
+
description: "View available commands, tools, and usage hints",
|
|
94458
|
+
category: "sys",
|
|
94459
|
+
source: "builtin"
|
|
94460
|
+
},
|
|
94461
|
+
{
|
|
94462
|
+
name: "/clear",
|
|
94463
|
+
description: "Clear conversation history and reset context window",
|
|
94464
|
+
category: "sys",
|
|
94465
|
+
source: "builtin"
|
|
94466
|
+
},
|
|
94467
|
+
{
|
|
94468
|
+
name: "/compact",
|
|
94469
|
+
description: "Purge execution trace and condense context window",
|
|
94470
|
+
category: "sys",
|
|
94471
|
+
source: "builtin"
|
|
94472
|
+
},
|
|
94473
|
+
{
|
|
94474
|
+
name: "/cost",
|
|
94475
|
+
description: "Inspect token breakdown and USD expenditure",
|
|
94476
|
+
category: "sys",
|
|
94477
|
+
source: "builtin"
|
|
94478
|
+
},
|
|
94479
|
+
{
|
|
94480
|
+
name: "/doctor",
|
|
94481
|
+
description: "Run diagnostic health checks on environment and tools",
|
|
94482
|
+
category: "sys",
|
|
94483
|
+
source: "builtin"
|
|
94484
|
+
},
|
|
94485
|
+
{
|
|
94486
|
+
name: "/init",
|
|
94487
|
+
description: "Initialize project instructions, settings and guidelines",
|
|
94488
|
+
category: "harness",
|
|
94489
|
+
source: "builtin"
|
|
94490
|
+
},
|
|
94491
|
+
{
|
|
94492
|
+
name: "/review",
|
|
94493
|
+
description: "Inspect staged git diff, verify test coverage & lint",
|
|
94494
|
+
category: "git",
|
|
94495
|
+
source: "builtin"
|
|
94496
|
+
},
|
|
94497
|
+
{
|
|
94498
|
+
name: "/commit",
|
|
94499
|
+
description: "Draft conventional commit message from staged hunks",
|
|
94500
|
+
category: "git",
|
|
94501
|
+
source: "builtin"
|
|
94502
|
+
},
|
|
94503
|
+
{
|
|
94504
|
+
name: "/terminal",
|
|
94505
|
+
description: "Run isolated bash execution in sandbox mirror",
|
|
94506
|
+
category: "sys",
|
|
94507
|
+
source: "builtin"
|
|
94508
|
+
},
|
|
94509
|
+
{
|
|
94510
|
+
name: "/revert",
|
|
94511
|
+
description: "Undo last agent file changes or restore git stash",
|
|
94512
|
+
category: "git",
|
|
94513
|
+
source: "builtin"
|
|
94514
|
+
},
|
|
94515
|
+
{
|
|
94516
|
+
name: "/resume",
|
|
94517
|
+
description: "Resume paused background agent run",
|
|
94518
|
+
category: "dev",
|
|
94519
|
+
source: "builtin"
|
|
94520
|
+
},
|
|
94521
|
+
{
|
|
94522
|
+
name: "/bug",
|
|
94523
|
+
description: "Report a bug or issue with diagnostic transcript",
|
|
94524
|
+
category: "sys",
|
|
94525
|
+
source: "builtin"
|
|
94526
|
+
},
|
|
94527
|
+
{
|
|
94528
|
+
name: "/config",
|
|
94529
|
+
description: "View or update local/global configuration settings",
|
|
94530
|
+
category: "sys",
|
|
94531
|
+
source: "builtin"
|
|
94532
|
+
},
|
|
94533
|
+
{
|
|
94534
|
+
name: "/login",
|
|
94535
|
+
description: "Authenticate account and refresh API credentials",
|
|
94536
|
+
category: "sys",
|
|
94537
|
+
source: "builtin"
|
|
94538
|
+
},
|
|
94539
|
+
{
|
|
94540
|
+
name: "/logout",
|
|
94541
|
+
description: "Sign out of current account and remove stored session",
|
|
94542
|
+
category: "sys",
|
|
94543
|
+
source: "builtin"
|
|
94544
|
+
}
|
|
94545
|
+
];
|
|
94546
|
+
});
|
|
94547
|
+
|
|
94112
94548
|
// ../../packages/app/src/services/write-slot-service.ts
|
|
94113
94549
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
94114
94550
|
|
|
@@ -94458,7 +94894,7 @@ var init_strategy_runtime = __esm(() => {
|
|
|
94458
94894
|
});
|
|
94459
94895
|
|
|
94460
94896
|
// ../../packages/app/src/services/supervisor-service.ts
|
|
94461
|
-
import { dirname as
|
|
94897
|
+
import { dirname as dirname20 } from "path";
|
|
94462
94898
|
function defaultWrapperArgv(agentId) {
|
|
94463
94899
|
return {
|
|
94464
94900
|
command: process.execPath,
|
|
@@ -94518,7 +94954,7 @@ class SupervisorService {
|
|
|
94518
94954
|
}
|
|
94519
94955
|
if (spec.tags.some((tag) => tag.startsWith("fleet:"))) {
|
|
94520
94956
|
await new FleetService({
|
|
94521
|
-
fs: createNodeFileSystem3(
|
|
94957
|
+
fs: createNodeFileSystem3(dirname20(dirname20(this.configDir)))
|
|
94522
94958
|
}).assertLaunchGroundTruth(spec.workspace);
|
|
94523
94959
|
}
|
|
94524
94960
|
const { command, args } = this.resolveCommand(spec);
|
|
@@ -94608,10 +95044,10 @@ class SupervisorService {
|
|
|
94608
95044
|
proc.handle.kill("SIGTERM");
|
|
94609
95045
|
let timedOut = false;
|
|
94610
95046
|
const exitPromise = proc.handle.exited;
|
|
94611
|
-
const timeout = new Promise((
|
|
95047
|
+
const timeout = new Promise((resolve17) => {
|
|
94612
95048
|
setTimeout(() => {
|
|
94613
95049
|
timedOut = true;
|
|
94614
|
-
|
|
95050
|
+
resolve17();
|
|
94615
95051
|
}, 3000);
|
|
94616
95052
|
});
|
|
94617
95053
|
await Promise.race([exitPromise, timeout]);
|
|
@@ -95011,7 +95447,7 @@ async function* followSystemEventsAfter(getDb, opts) {
|
|
|
95011
95447
|
await sleep3(FOLLOW_POLL_INTERVAL_MS);
|
|
95012
95448
|
}
|
|
95013
95449
|
}
|
|
95014
|
-
var FOLLOW_POLL_INTERVAL_MS = 100, FOLLOW_BATCH_SIZE = 512, sleep3 = (ms) => new Promise((
|
|
95450
|
+
var FOLLOW_POLL_INTERVAL_MS = 100, FOLLOW_BATCH_SIZE = 512, sleep3 = (ms) => new Promise((resolve17) => setTimeout(resolve17, ms));
|
|
95015
95451
|
var init_system_event_follow = __esm(() => {
|
|
95016
95452
|
init_src2();
|
|
95017
95453
|
});
|
|
@@ -95259,7 +95695,7 @@ var init_task_readiness = __esm(() => {
|
|
|
95259
95695
|
});
|
|
95260
95696
|
|
|
95261
95697
|
// ../../packages/app/src/services/task-scaffold.ts
|
|
95262
|
-
import { dirname as
|
|
95698
|
+
import { dirname as dirname21 } from "path";
|
|
95263
95699
|
|
|
95264
95700
|
class TaskScaffoldService {
|
|
95265
95701
|
ctx;
|
|
@@ -95302,7 +95738,7 @@ class TaskScaffoldService {
|
|
|
95302
95738
|
fileExists2 = false;
|
|
95303
95739
|
}
|
|
95304
95740
|
const mergeResult = mergeStubs(existingContent, stubs);
|
|
95305
|
-
const dir =
|
|
95741
|
+
const dir = dirname21(targetFile);
|
|
95306
95742
|
await this.ctx.fs.ensureDir(dir);
|
|
95307
95743
|
if (mergeResult.created > 0 || !fileExists2) {
|
|
95308
95744
|
await this.ctx.fs.writeFile(targetFile, mergeResult.content);
|
|
@@ -95384,7 +95820,7 @@ var init_task_size_precheck = __esm(() => {
|
|
|
95384
95820
|
});
|
|
95385
95821
|
|
|
95386
95822
|
// ../../packages/app/src/services/task-service.ts
|
|
95387
|
-
import { dirname as
|
|
95823
|
+
import { dirname as dirname22, isAbsolute as isAbsolute7, join as join31, relative as relative5 } from "path";
|
|
95388
95824
|
function renderRosterTable(rows) {
|
|
95389
95825
|
const header = `| WBS | Sub-task | Status |
|
|
95390
95826
|
| --- | -------- | ------ |`;
|
|
@@ -95524,7 +95960,8 @@ ${templateBackground}`;
|
|
|
95524
95960
|
...input.featureId !== undefined ? { feature_id: input.featureId } : {},
|
|
95525
95961
|
...input.parentWbs !== undefined ? { parent_wbs: input.parentWbs } : {},
|
|
95526
95962
|
...input.priority !== undefined ? { priority: input.priority } : {},
|
|
95527
|
-
...input.tags !== undefined && input.tags.length > 0 ? { tags: input.tags } : {}
|
|
95963
|
+
...input.tags !== undefined && input.tags.length > 0 ? { tags: input.tags } : {},
|
|
95964
|
+
...input.estimateHours !== undefined ? { estimate_hours: input.estimateHours } : {}
|
|
95528
95965
|
});
|
|
95529
95966
|
let status;
|
|
95530
95967
|
if (input.explicitStatus !== undefined) {
|
|
@@ -95624,7 +96061,8 @@ ${templateBackground}`;
|
|
|
95624
96061
|
done_forced: true,
|
|
95625
96062
|
done_reason: true,
|
|
95626
96063
|
ac_numbering: true,
|
|
95627
|
-
ac_altitude: true
|
|
96064
|
+
ac_altitude: true,
|
|
96065
|
+
estimate_hours: true
|
|
95628
96066
|
};
|
|
95629
96067
|
if (!(key2 in allowed)) {
|
|
95630
96068
|
throw new Error(`Field "${key2}" is not settable via update; allowed: ${Object.keys(allowed).join(", ")}.`);
|
|
@@ -95874,8 +96312,8 @@ ${templateBackground}`;
|
|
|
95874
96312
|
const featureId2 = fm.feature_id ?? fm["feature-id"];
|
|
95875
96313
|
if (!featureId2 || featureId2.length === 0)
|
|
95876
96314
|
return [];
|
|
95877
|
-
const tasksDir =
|
|
95878
|
-
const featuresDir =
|
|
96315
|
+
const tasksDir = dirname22(taskFilePath);
|
|
96316
|
+
const featuresDir = join31(tasksDir, "..", "features");
|
|
95879
96317
|
const featurePath = await (async () => {
|
|
95880
96318
|
try {
|
|
95881
96319
|
for (const name of await this.ctx.fs.readDir(featuresDir)) {
|
|
@@ -96019,7 +96457,8 @@ ${issues}`);
|
|
|
96019
96457
|
plan: item.plan,
|
|
96020
96458
|
acceptanceCriteria: item.acceptance_criteria,
|
|
96021
96459
|
priority: item.priority,
|
|
96022
|
-
tags: item.tags
|
|
96460
|
+
tags: item.tags,
|
|
96461
|
+
estimateHours: item.estimate_hours
|
|
96023
96462
|
});
|
|
96024
96463
|
try {
|
|
96025
96464
|
this.validateCandidateOrThrow(item.name, shape);
|
|
@@ -96186,8 +96625,8 @@ ${block}` : block);
|
|
|
96186
96625
|
return { wbs: hit.wbs, filePath: hit.filePath };
|
|
96187
96626
|
if (opts.strict === true)
|
|
96188
96627
|
return null;
|
|
96189
|
-
const
|
|
96190
|
-
const capturedWbs = /^(\d{4})_.+\.md$/.exec(
|
|
96628
|
+
const basename12 = filePath.split("/").pop() ?? "";
|
|
96629
|
+
const capturedWbs = /^(\d{4})_.+\.md$/.exec(basename12)?.[1];
|
|
96191
96630
|
if (capturedWbs) {
|
|
96192
96631
|
const taskPath = await this.locator.findPathByWbs(capturedWbs);
|
|
96193
96632
|
if (taskPath !== null)
|
|
@@ -96659,8 +97098,8 @@ var init_task_verdict = __esm(() => {
|
|
|
96659
97098
|
});
|
|
96660
97099
|
|
|
96661
97100
|
// ../../packages/app/src/services/token-ledger-service.ts
|
|
96662
|
-
import { closeSync as closeSync3, existsSync as
|
|
96663
|
-
import { join as
|
|
97101
|
+
import { closeSync as closeSync3, existsSync as existsSync9, fstatSync, openSync as openSync3, readSync } from "fs";
|
|
97102
|
+
import { join as join32 } from "path";
|
|
96664
97103
|
function isSparseToolActivity(events2) {
|
|
96665
97104
|
return events2.length === 0 || !events2.some((e) => TOOL_ACTIVITY_TYPES.has(e.type));
|
|
96666
97105
|
}
|
|
@@ -96711,7 +97150,7 @@ function parseLedgerLine(line) {
|
|
|
96711
97150
|
}
|
|
96712
97151
|
}
|
|
96713
97152
|
function tailTokenLedgerFile(filePath, limit, chunkSize = DEFAULT_CHUNK, before) {
|
|
96714
|
-
if (!
|
|
97153
|
+
if (!existsSync9(filePath)) {
|
|
96715
97154
|
return { events: [], truncated: false };
|
|
96716
97155
|
}
|
|
96717
97156
|
const target = limit + 1;
|
|
@@ -96761,7 +97200,7 @@ class TokenLedgerService {
|
|
|
96761
97200
|
ledgerPath;
|
|
96762
97201
|
chunkSize;
|
|
96763
97202
|
constructor(options) {
|
|
96764
|
-
this.ledgerPath = options.ledgerPath ??
|
|
97203
|
+
this.ledgerPath = options.ledgerPath ?? join32(options.cwd, TOKEN_LEDGER_RELATIVE_PATH);
|
|
96765
97204
|
this.chunkSize = options.chunkSize ?? DEFAULT_CHUNK;
|
|
96766
97205
|
}
|
|
96767
97206
|
get path() {
|
|
@@ -96802,12 +97241,12 @@ class TokenLedgerService {
|
|
|
96802
97241
|
var TOOL_ACTIVITY_TYPES, TOKEN_LEDGER_DEFAULT_LIMIT = 200, TOKEN_LEDGER_MAX_LIMIT = 1000, TOKEN_LEDGER_RELATIVE_PATH, DEFAULT_CHUNK;
|
|
96803
97242
|
var init_token_ledger_service = __esm(() => {
|
|
96804
97243
|
TOOL_ACTIVITY_TYPES = new Set(["read", "write", "bash", "grep", "glob"]);
|
|
96805
|
-
TOKEN_LEDGER_RELATIVE_PATH =
|
|
97244
|
+
TOKEN_LEDGER_RELATIVE_PATH = join32(".spur", "context", "token-ledger.jsonl");
|
|
96806
97245
|
DEFAULT_CHUNK = 64 * 1024;
|
|
96807
97246
|
});
|
|
96808
97247
|
|
|
96809
97248
|
// ../../packages/app/src/services/token-ledger-watcher.ts
|
|
96810
|
-
import { closeSync as closeSync4, existsSync as
|
|
97249
|
+
import { closeSync as closeSync4, existsSync as existsSync10, fstatSync as fstatSync2, openSync as openSync4, readSync as readSync2, watch } from "fs";
|
|
96811
97250
|
|
|
96812
97251
|
class TokenLedgerWatcher {
|
|
96813
97252
|
ledgerPath;
|
|
@@ -96828,7 +97267,7 @@ class TokenLedgerWatcher {
|
|
|
96828
97267
|
this.started = true;
|
|
96829
97268
|
this.offset = this.currentSize();
|
|
96830
97269
|
try {
|
|
96831
|
-
const target =
|
|
97270
|
+
const target = existsSync10(this.ledgerPath) ? this.ledgerPath : this.parentDir();
|
|
96832
97271
|
this.watcher = watch(target, () => this.schedulePoll());
|
|
96833
97272
|
} catch {
|
|
96834
97273
|
this.watcher = undefined;
|
|
@@ -96863,7 +97302,7 @@ class TokenLedgerWatcher {
|
|
|
96863
97302
|
return idx > 0 ? this.ledgerPath.slice(0, idx) : ".";
|
|
96864
97303
|
}
|
|
96865
97304
|
currentSize() {
|
|
96866
|
-
if (!
|
|
97305
|
+
if (!existsSync10(this.ledgerPath))
|
|
96867
97306
|
return 0;
|
|
96868
97307
|
try {
|
|
96869
97308
|
const fd = openSync4(this.ledgerPath, "r");
|
|
@@ -96885,7 +97324,7 @@ class TokenLedgerWatcher {
|
|
|
96885
97324
|
}, this.debounceMs);
|
|
96886
97325
|
}
|
|
96887
97326
|
pollNewBytes() {
|
|
96888
|
-
if (!
|
|
97327
|
+
if (!existsSync10(this.ledgerPath)) {
|
|
96889
97328
|
this.offset = 0;
|
|
96890
97329
|
this.carry = "";
|
|
96891
97330
|
return;
|
|
@@ -96930,7 +97369,7 @@ var init_token_ledger_watcher = __esm(() => {
|
|
|
96930
97369
|
});
|
|
96931
97370
|
|
|
96932
97371
|
// ../../packages/app/src/workflow/idea-handoff.ts
|
|
96933
|
-
import { resolve as
|
|
97372
|
+
import { resolve as resolve17 } from "path";
|
|
96934
97373
|
function processEvidence(r) {
|
|
96935
97374
|
return `exit=${r.exitCode ?? "null"}${r.signal ? ` signal=${r.signal}` : ""}: ${r.stderr.trim() || "no stderr"}`;
|
|
96936
97375
|
}
|
|
@@ -96940,12 +97379,12 @@ async function finalizeIdeaHandoff(options) {
|
|
|
96940
97379
|
const executor = options.processExecutor ?? new NodeProcessExecutor3;
|
|
96941
97380
|
const spurBin = options.spurBin ?? "spur";
|
|
96942
97381
|
const { runId, featureId: featureId2 } = options;
|
|
96943
|
-
const runDir =
|
|
96944
|
-
const batchPath =
|
|
96945
|
-
const resultPath =
|
|
96946
|
-
const orderPath =
|
|
96947
|
-
const readyPath =
|
|
96948
|
-
const reportPath =
|
|
97382
|
+
const runDir = resolve17(root, ".spur", "run");
|
|
97383
|
+
const batchPath = resolve17(runDir, `${runId}-idea-task-batch.json`);
|
|
97384
|
+
const resultPath = resolve17(runDir, `${runId}-idea-batch-create-result.json`);
|
|
97385
|
+
const orderPath = resolve17(runDir, `${runId}-idea-task-order.json`);
|
|
97386
|
+
const readyPath = resolve17(runDir, `${runId}-idea-ready.json`);
|
|
97387
|
+
const reportPath = resolve17(runDir, `${runId}-idea-handoff.md`);
|
|
96949
97388
|
const spurSplit = splitLaunchCommand(spurBin, 'idea-handoff "spurBin"');
|
|
96950
97389
|
if ("error" in spurSplit) {
|
|
96951
97390
|
return {
|
|
@@ -97738,7 +98177,7 @@ var init_progress_follow = __esm(() => {
|
|
|
97738
98177
|
});
|
|
97739
98178
|
|
|
97740
98179
|
// ../../packages/app/src/workflow/trace-writer.ts
|
|
97741
|
-
import { dirname as
|
|
98180
|
+
import { dirname as dirname23, join as join33 } from "path";
|
|
97742
98181
|
|
|
97743
98182
|
class WorkflowTraceWriter {
|
|
97744
98183
|
path;
|
|
@@ -97746,7 +98185,7 @@ class WorkflowTraceWriter {
|
|
|
97746
98185
|
pending = Promise.resolve();
|
|
97747
98186
|
constructor(cwd, runId) {
|
|
97748
98187
|
const safeRunId = runId.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
97749
|
-
this.path =
|
|
98188
|
+
this.path = join33(cwd, ".spur", "workflow", `${safeRunId}.jsonl`);
|
|
97750
98189
|
}
|
|
97751
98190
|
attach(bus) {
|
|
97752
98191
|
bus.on("workflow.run.started", (event) => this.enqueue("workflow.run.started", event));
|
|
@@ -97772,7 +98211,7 @@ class WorkflowTraceWriter {
|
|
|
97772
98211
|
`;
|
|
97773
98212
|
this.pending = this.pending.then(async () => {
|
|
97774
98213
|
const fs3 = createNodeFileSystem3();
|
|
97775
|
-
await fs3.ensureDir(
|
|
98214
|
+
await fs3.ensureDir(dirname23(this.path));
|
|
97776
98215
|
await fs3.appendFile(this.path, line);
|
|
97777
98216
|
});
|
|
97778
98217
|
}
|
|
@@ -97867,6 +98306,7 @@ __export(exports_src2, {
|
|
|
97867
98306
|
setPortProbeForTests: () => setPortProbeForTests,
|
|
97868
98307
|
setDetachedServeSpawnForTests: () => setDetachedServeSpawnForTests,
|
|
97869
98308
|
schedulerJobTimeoutEnvName: () => schedulerJobTimeoutEnvName,
|
|
98309
|
+
scanMarkdownCommands: () => scanMarkdownCommands,
|
|
97870
98310
|
satisfies: () => satisfies,
|
|
97871
98311
|
runIdeaHandoffCli: () => runIdeaHandoffCli,
|
|
97872
98312
|
runHistoryReport: () => runHistoryReport,
|
|
@@ -97939,7 +98379,9 @@ __export(exports_src2, {
|
|
|
97939
98379
|
normalizeLegacyTimeoutMs: () => normalizeLegacyTimeoutMs,
|
|
97940
98380
|
materializeRoster: () => materializeRoster,
|
|
97941
98381
|
looksLikeOpaqueId: () => looksLikeOpaqueId,
|
|
98382
|
+
loadSlashCommands: () => loadSlashCommands,
|
|
97942
98383
|
isSystemEventEnvelopeV2: () => isSystemEventEnvelopeV2,
|
|
98384
|
+
isSchedulerCustomActiveConflict: () => isSchedulerCustomActiveConflict,
|
|
97943
98385
|
isRecordAuthoredReview: () => isRecordAuthoredReview,
|
|
97944
98386
|
isPortLive: () => isPortLive,
|
|
97945
98387
|
isPortAvailable: () => isPortAvailable,
|
|
@@ -97957,7 +98399,10 @@ __export(exports_src2, {
|
|
|
97957
98399
|
handleHistoryRefreshJob: () => handleHistoryRefreshJob,
|
|
97958
98400
|
gtdStrategy: () => gtdStrategy,
|
|
97959
98401
|
getLatestSystemEventSequence: () => getLatestSystemEventSequence,
|
|
98402
|
+
getBuiltinClaudeCommands: () => getBuiltinClaudeCommands,
|
|
97960
98403
|
genericSystemEventCatalogEntry: () => genericSystemEventCatalogEntry,
|
|
98404
|
+
generateSlashCommandsFile: () => generateSlashCommandsFile,
|
|
98405
|
+
generateSlashCommands: () => generateSlashCommands,
|
|
97961
98406
|
formatNoopMessage: () => formatNoopMessage,
|
|
97962
98407
|
formatIssue: () => formatIssue,
|
|
97963
98408
|
formatDenialMessage: () => formatDenialMessage,
|
|
@@ -97980,6 +98425,7 @@ __export(exports_src2, {
|
|
|
97980
98425
|
encodeRunListCursor: () => encodeRunListCursor,
|
|
97981
98426
|
drainPendingAgentQuotaUpdates: () => drainPendingAgentQuotaUpdates,
|
|
97982
98427
|
deriveVerdict: () => deriveVerdict,
|
|
98428
|
+
deduceCommandCategory: () => deduceCommandCategory,
|
|
97983
98429
|
decorateWorkflowEvent: () => decorateWorkflowEvent,
|
|
97984
98430
|
decodeRunListCursor: () => decodeRunListCursor,
|
|
97985
98431
|
decisionKindForGate: () => decisionKindForGate,
|
|
@@ -98042,6 +98488,7 @@ __export(exports_src2, {
|
|
|
98042
98488
|
SystemEventEmitter: () => SystemEventEmitter,
|
|
98043
98489
|
SupervisorService: () => SupervisorService,
|
|
98044
98490
|
StrategyRuntime: () => StrategyRuntime,
|
|
98491
|
+
SlashCommandsService: () => SlashCommandsService,
|
|
98045
98492
|
SectionMutationError: () => SectionMutationError,
|
|
98046
98493
|
SYSTEM_EVENT_UNCATALOGED_WARN_EVENT: () => SYSTEM_EVENT_UNCATALOGED_WARN_EVENT,
|
|
98047
98494
|
SYSTEM_EVENT_STREAMED_NAMES: () => SYSTEM_EVENT_STREAMED_NAMES,
|
|
@@ -98054,6 +98501,7 @@ __export(exports_src2, {
|
|
|
98054
98501
|
STRATEGIES: () => STRATEGIES,
|
|
98055
98502
|
SCHEDULER_CUSTOM_TIMEOUT_MS: () => SCHEDULER_CUSTOM_TIMEOUT_MS,
|
|
98056
98503
|
SCHEDULER_CUSTOM_JOB: () => SCHEDULER_CUSTOM_JOB,
|
|
98504
|
+
SCHEDULER_CUSTOM_ACTIVE_INDEX: () => SCHEDULER_CUSTOM_ACTIVE_INDEX,
|
|
98057
98505
|
RunStoreService: () => RunStoreService,
|
|
98058
98506
|
RunStoreNotFoundError: () => RunStoreNotFoundError,
|
|
98059
98507
|
RunStoreBadCursorError: () => RunStoreBadCursorError,
|
|
@@ -98129,6 +98577,7 @@ __export(exports_src2, {
|
|
|
98129
98577
|
CommandGateActionRunner: () => CommandGateActionRunner,
|
|
98130
98578
|
CHILD_KILL_GRACE_MS: () => CHILD_KILL_GRACE_MS,
|
|
98131
98579
|
BusPlanningEventEmitter: () => BusPlanningEventEmitter,
|
|
98580
|
+
BUILTIN_CLAUDE_COMMANDS: () => BUILTIN_CLAUDE_COMMANDS,
|
|
98132
98581
|
AgentService: () => AgentService,
|
|
98133
98582
|
AgentRunActionRunner: () => AgentRunActionRunner,
|
|
98134
98583
|
AgentExecutionLifecycle: () => AgentExecutionLifecycle,
|
|
@@ -98179,6 +98628,7 @@ var init_src3 = __esm(async () => {
|
|
|
98179
98628
|
init_run_session_observer();
|
|
98180
98629
|
init_run_store_service();
|
|
98181
98630
|
init_scheduler_custom_job_service();
|
|
98631
|
+
init_slash_commands_service();
|
|
98182
98632
|
init_strategy_runtime();
|
|
98183
98633
|
init_supervisor_service();
|
|
98184
98634
|
init_system_event_catch_all();
|
|
@@ -105979,10 +106429,10 @@ init_src2();
|
|
|
105979
106429
|
init_dist7();
|
|
105980
106430
|
init_dist5();
|
|
105981
106431
|
await init_src3();
|
|
105982
|
-
import { existsSync as
|
|
106432
|
+
import { existsSync as existsSync11, readFileSync as readFileSync11 } from "fs";
|
|
105983
106433
|
import { rm, writeFile } from "fs/promises";
|
|
105984
106434
|
import { tmpdir as tmpdir3 } from "os";
|
|
105985
|
-
import { dirname as
|
|
106435
|
+
import { dirname as dirname24, join as join34 } from "path";
|
|
105986
106436
|
|
|
105987
106437
|
// src/colors.ts
|
|
105988
106438
|
var CODES = {
|
|
@@ -106397,6 +106847,11 @@ var spur_config_schema_default = {
|
|
|
106397
106847
|
type: "object",
|
|
106398
106848
|
description: "Release knobs for bump-ver/drop-tags. Defaults: -v separator, publish.yml workflow, chore(release) commit, gh run list limit 5.",
|
|
106399
106849
|
properties: {
|
|
106850
|
+
aggregatePackage: {
|
|
106851
|
+
type: "string",
|
|
106852
|
+
minLength: 1,
|
|
106853
|
+
description: 'Package whose own release tag is the publish trigger for the aggregate (--all) path, as an unscoped package id (e.g. "spur" for @gobing-ai/spur). Default: the package whose full name equals the workspace root manifest name.'
|
|
106854
|
+
},
|
|
106400
106855
|
tagVersionSeparator: {
|
|
106401
106856
|
type: "string",
|
|
106402
106857
|
minLength: 1,
|
|
@@ -106942,9 +107397,9 @@ init_src2();
|
|
|
106942
107397
|
await init_src3();
|
|
106943
107398
|
|
|
106944
107399
|
// src/workflow/resolve-spur-bin.ts
|
|
106945
|
-
import { basename as
|
|
107400
|
+
import { basename as basename12 } from "path";
|
|
106946
107401
|
function resolveSpurBinFrom(launch) {
|
|
106947
|
-
const runtime =
|
|
107402
|
+
const runtime = basename12(launch.execPath).toLowerCase().replace(/\.exe$/, "");
|
|
106948
107403
|
const isJsRuntime = runtime === "bun" || runtime === "node";
|
|
106949
107404
|
if (isJsRuntime && launch.mainModule) {
|
|
106950
107405
|
return `${launch.execPath} ${launch.mainModule}`;
|
|
@@ -107202,7 +107657,7 @@ ${result.content}`);
|
|
|
107202
107657
|
'Sections replace, with one exception: `--section "Q&A"` APPENDS a timestamped',
|
|
107203
107658
|
"`#### Q&A entry \u2014 <ISO>` block. Start the body with `<!-- qa:replace -->` to replace it wholesale."
|
|
107204
107659
|
].join(`
|
|
107205
|
-
`)).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) => {
|
|
107660
|
+
`)).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) => {
|
|
107206
107661
|
const svc = await makeService(context4, options.folder, options.lifecycle === false);
|
|
107207
107662
|
try {
|
|
107208
107663
|
if (options.assignee !== undefined) {
|
|
@@ -107240,9 +107695,9 @@ ${result.content}`);
|
|
|
107240
107695
|
}
|
|
107241
107696
|
context4.output.write(`Updated section '${options.section}' in task ${result.ref.id}`);
|
|
107242
107697
|
}
|
|
107243
|
-
} else if (options.feature !== undefined || options.priority !== undefined || options.acNumbering !== undefined || options.acAltitude !== undefined) {
|
|
107244
|
-
const key2 = options.feature !== undefined ? "feature_id" : options.priority !== undefined ? "priority" : options.acNumbering !== undefined ? "ac_numbering" : "ac_altitude";
|
|
107245
|
-
const value = options.feature ?? options.priority ?? options.acNumbering ?? options.acAltitude ?? "";
|
|
107698
|
+
} else if (options.feature !== undefined || options.priority !== undefined || options.acNumbering !== undefined || options.acAltitude !== undefined || options.estimateHours !== undefined) {
|
|
107699
|
+
const key2 = options.feature !== undefined ? "feature_id" : options.priority !== undefined ? "priority" : options.acNumbering !== undefined ? "ac_numbering" : options.acAltitude !== undefined ? "ac_altitude" : "estimate_hours";
|
|
107700
|
+
const value = options.feature ?? options.priority ?? options.acNumbering ?? options.acAltitude ?? options.estimateHours ?? "";
|
|
107246
107701
|
const result = await svc.updateField(wbs, key2, value);
|
|
107247
107702
|
if (options.json) {
|
|
107248
107703
|
context4.output.write(toEnvelopeJson(result, { enveloped: options.jsonEnvelope }));
|
|
@@ -107272,7 +107727,7 @@ ${result.content}`);
|
|
|
107272
107727
|
let forcedDoneVerdict;
|
|
107273
107728
|
if (status === "done") {
|
|
107274
107729
|
const current = await svc.show(wbs);
|
|
107275
|
-
const verdictDir = options.verdictDir ??
|
|
107730
|
+
const verdictDir = options.verdictDir ?? join34(context4.cwd, ".spur", "run");
|
|
107276
107731
|
const loaded = await readVerdictArtifact(context4.fs, verdictDir, wbs);
|
|
107277
107732
|
const guardOutcome = evaluateDoneTransition({
|
|
107278
107733
|
wbs,
|
|
@@ -107334,7 +107789,7 @@ ${result.content}`);
|
|
|
107334
107789
|
}
|
|
107335
107790
|
}
|
|
107336
107791
|
} else {
|
|
107337
|
-
context4.output.error("Either <status>, --section/--from-file, or --feature/--priority is required");
|
|
107792
|
+
context4.output.error("Either <status>, --section/--from-file, or a field flag (--feature/--priority/--ac-numbering/--ac-altitude/--estimate-hours) is required");
|
|
107338
107793
|
context4.setExitCode(2);
|
|
107339
107794
|
}
|
|
107340
107795
|
} catch (err) {
|
|
@@ -107508,7 +107963,7 @@ ${result.content}`);
|
|
|
107508
107963
|
dryRun,
|
|
107509
107964
|
projectRoot: context4.cwd,
|
|
107510
107965
|
write: async (filePath, wbs, section, newBody) => {
|
|
107511
|
-
const ref = { kind: "task", id: wbs, filePath, folder:
|
|
107966
|
+
const ref = { kind: "task", id: wbs, filePath, folder: dirname24(filePath) };
|
|
107512
107967
|
const ws = new PlanningWriteService({ fs: context4.fs, emitter: makePlanningEmitter(context4) });
|
|
107513
107968
|
await ws.updateSection(ref, section, newBody);
|
|
107514
107969
|
}
|
|
@@ -107582,7 +108037,7 @@ ${result.content}`);
|
|
|
107582
108037
|
agentSelector: options.agent,
|
|
107583
108038
|
cwd: context4.cwd
|
|
107584
108039
|
});
|
|
107585
|
-
tmpPath =
|
|
108040
|
+
tmpPath = join34(tmpdir3(), `spur-ready-batch-${Date.now()}-${Math.random().toString(36).slice(2, 8)}.json`);
|
|
107586
108041
|
await writeFile(tmpPath, `${JSON.stringify(prepared.items, null, 2)}
|
|
107587
108042
|
`);
|
|
107588
108043
|
batchFile = tmpPath;
|
|
@@ -108015,15 +108470,15 @@ function loadTemplateBodies(projectRoot, variant) {
|
|
|
108015
108470
|
if (cached2 !== undefined)
|
|
108016
108471
|
return cached2;
|
|
108017
108472
|
let bodies = {};
|
|
108018
|
-
const localPath =
|
|
108019
|
-
if (
|
|
108020
|
-
bodies = extractTemplateBodies(
|
|
108473
|
+
const localPath = join34(projectRoot, ".spur", "tasks", "templates", `${variant}.md`);
|
|
108474
|
+
if (existsSync11(localPath)) {
|
|
108475
|
+
bodies = extractTemplateBodies(readFileSync11(localPath, "utf8"));
|
|
108021
108476
|
} else {
|
|
108022
108477
|
const root = bundledConfigRoot();
|
|
108023
108478
|
if (root !== null) {
|
|
108024
|
-
const templatePath =
|
|
108025
|
-
if (
|
|
108026
|
-
bodies = extractTemplateBodies(
|
|
108479
|
+
const templatePath = join34(root, "templates", "task", `${variant}.md`);
|
|
108480
|
+
if (existsSync11(templatePath)) {
|
|
108481
|
+
bodies = extractTemplateBodies(readFileSync11(templatePath, "utf8"));
|
|
108027
108482
|
}
|
|
108028
108483
|
}
|
|
108029
108484
|
}
|
|
@@ -108079,7 +108534,7 @@ async function loadSectionMatrixUncached(projectRoot) {
|
|
|
108079
108534
|
}
|
|
108080
108535
|
const root = bundledConfigRoot();
|
|
108081
108536
|
if (root !== null) {
|
|
108082
|
-
const matrixPath =
|
|
108537
|
+
const matrixPath = join34(root, "tasks", "section-matrix.yaml");
|
|
108083
108538
|
if (await fs3.exists(matrixPath)) {
|
|
108084
108539
|
const data = await loadStructuredSpurConfig(matrixPath, {
|
|
108085
108540
|
validateJsonSchema: true,
|
|
@@ -108090,7 +108545,7 @@ async function loadSectionMatrixUncached(projectRoot) {
|
|
|
108090
108545
|
}
|
|
108091
108546
|
throw new Error(`no canonical section-matrix found for task section authority (F92 R1); tried:
|
|
108092
108547
|
` + ` - ${localPath}
|
|
108093
|
-
` + (root !== null ? ` - ${
|
|
108548
|
+
` + (root !== null ? ` - ${join34(root, "tasks", "section-matrix.yaml")}
|
|
108094
108549
|
` : "") + "copy/generate section-matrix.yaml from the canonical build-time matrix asset (repo `config` `tasks` tree) into one of those paths");
|
|
108095
108550
|
}
|
|
108096
108551
|
|
|
@@ -108468,15 +108923,15 @@ function parseTimeout(raw) {
|
|
|
108468
108923
|
return n3;
|
|
108469
108924
|
}
|
|
108470
108925
|
function loopSleep(ms, signal) {
|
|
108471
|
-
return new Promise((
|
|
108926
|
+
return new Promise((resolve18) => {
|
|
108472
108927
|
if (signal?.aborted) {
|
|
108473
|
-
|
|
108928
|
+
resolve18();
|
|
108474
108929
|
return;
|
|
108475
108930
|
}
|
|
108476
|
-
const timer = setTimeout(
|
|
108931
|
+
const timer = setTimeout(resolve18, ms);
|
|
108477
108932
|
signal?.addEventListener("abort", () => {
|
|
108478
108933
|
clearTimeout(timer);
|
|
108479
|
-
|
|
108934
|
+
resolve18();
|
|
108480
108935
|
}, { once: true });
|
|
108481
108936
|
});
|
|
108482
108937
|
}
|
|
@@ -108796,13 +109251,13 @@ function waitFail(context4, options, code, message) {
|
|
|
108796
109251
|
// src/release-ops.ts
|
|
108797
109252
|
init_src();
|
|
108798
109253
|
init_dist5();
|
|
108799
|
-
import { existsSync as
|
|
108800
|
-
import { join as
|
|
109254
|
+
import { existsSync as existsSync13, readdirSync as readdirSync8, readFileSync as readFileSync12 } from "fs";
|
|
109255
|
+
import { join as join36 } from "path";
|
|
108801
109256
|
|
|
108802
109257
|
// src/version-carriers.ts
|
|
108803
109258
|
init_zod();
|
|
108804
|
-
import { existsSync as
|
|
108805
|
-
import { join as
|
|
109259
|
+
import { existsSync as existsSync12 } from "fs";
|
|
109260
|
+
import { join as join35 } from "path";
|
|
108806
109261
|
var defaultLiteralProbe = { file: "src/config.ts", identifier: "binaryVersion" };
|
|
108807
109262
|
var registry2 = new Map;
|
|
108808
109263
|
function registerCarrierType(type, def) {
|
|
@@ -108838,7 +109293,7 @@ function findLiteralProbe(carriers, packageDir) {
|
|
|
108838
109293
|
return;
|
|
108839
109294
|
}
|
|
108840
109295
|
async function readJson(path9) {
|
|
108841
|
-
if (!
|
|
109296
|
+
if (!existsSync12(path9))
|
|
108842
109297
|
return null;
|
|
108843
109298
|
try {
|
|
108844
109299
|
return JSON.parse(await Bun.file(path9).text());
|
|
@@ -108854,7 +109309,7 @@ registerCarrierType("plugin-manifest", {
|
|
|
108854
109309
|
schema: PluginManifestCarrierSchema,
|
|
108855
109310
|
async syncRepoWide(carrier, { repoRoot }, version3, staged, output2) {
|
|
108856
109311
|
for (const rel of carrier.paths) {
|
|
108857
|
-
const absPath =
|
|
109312
|
+
const absPath = join35(repoRoot, rel);
|
|
108858
109313
|
const manifest = await readJson(absPath);
|
|
108859
109314
|
if (manifest === null) {
|
|
108860
109315
|
output2.write(` \u26A0 ${rel}: not found or malformed \u2014 skipping`);
|
|
@@ -108884,6 +109339,19 @@ var SEMVER = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z-.]+)?(?:\+[0-9A-Za-z-.]+)?$/;
|
|
|
108884
109339
|
function releaseTag(config4, version3) {
|
|
108885
109340
|
return `${config4.packageName}${config4.tagVersionSeparator}${version3}`;
|
|
108886
109341
|
}
|
|
109342
|
+
function resolveAggregatePackage(aggregatePackage, packages, rootName) {
|
|
109343
|
+
if (aggregatePackage !== undefined) {
|
|
109344
|
+
const config4 = packages.get(aggregatePackage);
|
|
109345
|
+
if (config4 === undefined) {
|
|
109346
|
+
throw new Error(`unknown builder.bump-ver.aggregatePackage "${aggregatePackage}". Package IDs: ${[...packages.keys()].join(", ")}`);
|
|
109347
|
+
}
|
|
109348
|
+
return config4;
|
|
109349
|
+
}
|
|
109350
|
+
return [...packages.values()].find((c3) => c3.packageName === rootName);
|
|
109351
|
+
}
|
|
109352
|
+
function resolveAggregateTag(ctx, version3) {
|
|
109353
|
+
return ctx.aggregate === undefined ? `${ctx.rootName}-v${version3}` : releaseTag(ctx.aggregate, version3);
|
|
109354
|
+
}
|
|
108887
109355
|
async function run(repoRoot, cmd) {
|
|
108888
109356
|
const executor = new NodeProcessExecutor3({ defaultTimeout: 30000, defaultMaxOutput: 512000 });
|
|
108889
109357
|
const result = await executor.run({
|
|
@@ -108925,7 +109393,7 @@ async function readJson2(path9) {
|
|
|
108925
109393
|
function rootWorkspaceGlobs(repoRoot) {
|
|
108926
109394
|
let root;
|
|
108927
109395
|
try {
|
|
108928
|
-
root = JSON.parse(
|
|
109396
|
+
root = JSON.parse(readFileSync12(join36(repoRoot, "package.json"), "utf8"));
|
|
108929
109397
|
} catch {
|
|
108930
109398
|
return [];
|
|
108931
109399
|
}
|
|
@@ -108942,11 +109410,11 @@ function workspaceDirs(repoRoot) {
|
|
|
108942
109410
|
if (!glob.endsWith("/*"))
|
|
108943
109411
|
continue;
|
|
108944
109412
|
const parent = glob.slice(0, -2);
|
|
108945
|
-
const parentAbs =
|
|
108946
|
-
if (!
|
|
109413
|
+
const parentAbs = join36(repoRoot, parent);
|
|
109414
|
+
if (!existsSync13(parentAbs))
|
|
108947
109415
|
continue;
|
|
108948
|
-
for (const entry of
|
|
108949
|
-
if (entry.isDirectory() &&
|
|
109416
|
+
for (const entry of readdirSync8(parentAbs, { withFileTypes: true })) {
|
|
109417
|
+
if (entry.isDirectory() && existsSync13(join36(parentAbs, entry.name, "package.json"))) {
|
|
108950
109418
|
dirs.push(`${parent}/${entry.name}`);
|
|
108951
109419
|
}
|
|
108952
109420
|
}
|
|
@@ -108954,14 +109422,14 @@ function workspaceDirs(repoRoot) {
|
|
|
108954
109422
|
return dirs;
|
|
108955
109423
|
}
|
|
108956
109424
|
async function releaseContext(repoRoot, bumpVerConfig = resolveBuilderBumpVerConfig(null)) {
|
|
108957
|
-
const rootManifest = await readJson2(
|
|
109425
|
+
const rootManifest = await readJson2(join36(repoRoot, "package.json"));
|
|
108958
109426
|
if (rootManifest === null) {
|
|
108959
109427
|
throw new Error(`no package.json found at ${repoRoot} \u2014 builder operates on a package workspace root.`);
|
|
108960
109428
|
}
|
|
108961
109429
|
const rootName = typeof rootManifest.name === "string" ? rootManifest.name : "root";
|
|
108962
109430
|
const workspaces2 = [];
|
|
108963
109431
|
for (const dir of workspaceDirs(repoRoot)) {
|
|
108964
|
-
const manifest = await readJson2(
|
|
109432
|
+
const manifest = await readJson2(join36(repoRoot, dir, "package.json"));
|
|
108965
109433
|
if (manifest === null)
|
|
108966
109434
|
continue;
|
|
108967
109435
|
workspaces2.push({
|
|
@@ -108976,8 +109444,8 @@ async function releaseContext(repoRoot, bumpVerConfig = resolveBuilderBumpVerCon
|
|
|
108976
109444
|
const packages = new Map;
|
|
108977
109445
|
for (const ws of workspaces2) {
|
|
108978
109446
|
const probe = findLiteralProbe(versionCarriers, ws.dir) ?? defaultLiteralProbe;
|
|
108979
|
-
const literalProbe =
|
|
108980
|
-
const versionSourceFile =
|
|
109447
|
+
const literalProbe = join36(ws.dir, probe.file);
|
|
109448
|
+
const versionSourceFile = existsSync13(join36(repoRoot, literalProbe)) ? literalProbe : undefined;
|
|
108981
109449
|
packages.set(shortName(ws.name), {
|
|
108982
109450
|
packageDir: ws.dir,
|
|
108983
109451
|
packageName: ws.name,
|
|
@@ -109005,15 +109473,17 @@ async function releaseContext(repoRoot, bumpVerConfig = resolveBuilderBumpVerCon
|
|
|
109005
109473
|
}
|
|
109006
109474
|
}
|
|
109007
109475
|
}
|
|
109008
|
-
|
|
109476
|
+
const aggregate2 = resolveAggregatePackage(bumpVerConfig.aggregatePackage, packages, rootName);
|
|
109477
|
+
if (aggregate2 !== undefined)
|
|
109478
|
+
pinnedNames.add(aggregate2.packageName);
|
|
109009
109479
|
const allPackages = [...packages.values()].filter((c3) => pinnedNames.has(c3.packageName));
|
|
109010
|
-
return { repoRoot, rootName, packages, allPackages, versionCarriers };
|
|
109480
|
+
return { repoRoot, rootName, aggregate: aggregate2, packages, allPackages, versionCarriers };
|
|
109011
109481
|
}
|
|
109012
109482
|
async function updateWorkspacePins(ctx, pkgName, oldVersion, newVersion, output2) {
|
|
109013
109483
|
const changed = [];
|
|
109014
109484
|
for (const dir of workspaceDirs(ctx.repoRoot)) {
|
|
109015
109485
|
const relPath = `${dir}/package.json`;
|
|
109016
|
-
const manifest = await readJson2(
|
|
109486
|
+
const manifest = await readJson2(join36(ctx.repoRoot, relPath));
|
|
109017
109487
|
if (manifest === null)
|
|
109018
109488
|
continue;
|
|
109019
109489
|
let dirty = false;
|
|
@@ -109030,7 +109500,7 @@ async function updateWorkspacePins(ctx, pkgName, oldVersion, newVersion, output2
|
|
|
109030
109500
|
}
|
|
109031
109501
|
}
|
|
109032
109502
|
if (dirty) {
|
|
109033
|
-
await Bun.write(
|
|
109503
|
+
await Bun.write(join36(ctx.repoRoot, relPath), `${JSON.stringify(manifest, null, 4)}
|
|
109034
109504
|
`);
|
|
109035
109505
|
output2.write(` \u21B3 ${relPath}: ${pkgName} workspace pin ${oldVersion} \u2192 ${newVersion}`);
|
|
109036
109506
|
changed.push(relPath);
|
|
@@ -109039,7 +109509,7 @@ async function updateWorkspacePins(ctx, pkgName, oldVersion, newVersion, output2
|
|
|
109039
109509
|
return changed;
|
|
109040
109510
|
}
|
|
109041
109511
|
async function updateVersionSourceFile(ctx, filePath, previous, next, output2, identifier = "binaryVersion") {
|
|
109042
|
-
const absPath =
|
|
109512
|
+
const absPath = join36(ctx.repoRoot, filePath);
|
|
109043
109513
|
const content = await Bun.file(absPath).text();
|
|
109044
109514
|
const pattern = new RegExp(`(${identifier.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}:\\s*['"])\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z-.]+)?(?:\\+[0-9A-Za-z-.]+)?(['"])`);
|
|
109045
109515
|
const updated = content.replace(pattern, `$1${next}$2`);
|
|
@@ -109057,7 +109527,7 @@ async function syncExtraCarriers(ctx, version3, staged, output2) {
|
|
|
109057
109527
|
}
|
|
109058
109528
|
}
|
|
109059
109529
|
async function syncMarketplaceAndPlugins(ctx, version3, staged, output2) {
|
|
109060
|
-
const marketplacePath =
|
|
109530
|
+
const marketplacePath = join36(ctx.repoRoot, ".claude-plugin", "marketplace.json");
|
|
109061
109531
|
const marketplaceFile = Bun.file(marketplacePath);
|
|
109062
109532
|
if (!await marketplaceFile.exists())
|
|
109063
109533
|
return;
|
|
@@ -109082,7 +109552,7 @@ async function syncMarketplaceAndPlugins(ctx, version3, staged, output2) {
|
|
|
109082
109552
|
output2.write(`Bumped marketplace plugins to ${version3}`);
|
|
109083
109553
|
}
|
|
109084
109554
|
for (const entry of plugins) {
|
|
109085
|
-
const pluginJsonPath =
|
|
109555
|
+
const pluginJsonPath = join36(ctx.repoRoot, entry.source, "plugin.json");
|
|
109086
109556
|
const pluginJson = await readJson2(pluginJsonPath);
|
|
109087
109557
|
if (pluginJson === null) {
|
|
109088
109558
|
output2.write(` \u26A0 plugin.json not found at ${entry.source}/plugin.json \u2014 skipping`);
|
|
@@ -109135,7 +109605,7 @@ async function bumpVersion(ctx, config4, version3, options, output2) {
|
|
|
109135
109605
|
if (await npmViewVersion(ctx.repoRoot, config4.packageName, version3)) {
|
|
109136
109606
|
throw new Error(`${config4.packageName}@${version3} is already published on npm. Use a new version.`);
|
|
109137
109607
|
}
|
|
109138
|
-
const manifestPath =
|
|
109608
|
+
const manifestPath = join36(ctx.repoRoot, config4.packageDir, "package.json");
|
|
109139
109609
|
const manifest = await readJson2(manifestPath);
|
|
109140
109610
|
if (manifest === null)
|
|
109141
109611
|
throw new Error(`missing or malformed manifest at ${config4.packageDir}/package.json`);
|
|
@@ -109153,8 +109623,8 @@ async function bumpVersion(ctx, config4, version3, options, output2) {
|
|
|
109153
109623
|
staged.push(...await updateWorkspacePins(ctx, config4.packageName, previous, version3, output2));
|
|
109154
109624
|
await syncMarketplaceAndPlugins(ctx, version3, staged, output2);
|
|
109155
109625
|
await syncExtraCarriers(ctx, version3, staged, output2);
|
|
109156
|
-
const lockPath =
|
|
109157
|
-
if (
|
|
109626
|
+
const lockPath = join36(ctx.repoRoot, "bun.lock");
|
|
109627
|
+
if (existsSync13(lockPath) && Bun.file(lockPath).size > 0)
|
|
109158
109628
|
staged.push("bun.lock");
|
|
109159
109629
|
await git2(ctx.repoRoot, ["add", ...staged]);
|
|
109160
109630
|
const message = `${config4.releaseCommitType}(${config4.releaseCommitScope}): ${config4.releaseCommitSubject(version3)}`;
|
|
@@ -109188,7 +109658,7 @@ async function bumpAll(ctx, version3, options, output2) {
|
|
|
109188
109658
|
}
|
|
109189
109659
|
const branch = await assertCleanTreeOnBranch(ctx);
|
|
109190
109660
|
const configs = ctx.allPackages;
|
|
109191
|
-
const aggregateTag =
|
|
109661
|
+
const aggregateTag = resolveAggregateTag(ctx, version3);
|
|
109192
109662
|
const existingLocal = new Set((await git2(ctx.repoRoot, ["tag", "-l"])).split(`
|
|
109193
109663
|
`).filter(Boolean));
|
|
109194
109664
|
const allTags = [aggregateTag, ...configs.map((c3) => releaseTag(c3, version3))];
|
|
@@ -109209,7 +109679,7 @@ async function bumpAll(ctx, version3, options, output2) {
|
|
|
109209
109679
|
}
|
|
109210
109680
|
const staged = [];
|
|
109211
109681
|
for (const config4 of configs) {
|
|
109212
|
-
const manifestPath =
|
|
109682
|
+
const manifestPath = join36(ctx.repoRoot, config4.packageDir, "package.json");
|
|
109213
109683
|
const manifest = await readJson2(manifestPath);
|
|
109214
109684
|
if (manifest === null) {
|
|
109215
109685
|
throw new Error(`missing or malformed manifest at ${config4.packageDir}/package.json`);
|
|
@@ -109229,8 +109699,8 @@ async function bumpAll(ctx, version3, options, output2) {
|
|
|
109229
109699
|
}
|
|
109230
109700
|
await syncMarketplaceAndPlugins(ctx, version3, staged, output2);
|
|
109231
109701
|
await syncExtraCarriers(ctx, version3, staged, output2);
|
|
109232
|
-
const lockPath =
|
|
109233
|
-
if (
|
|
109702
|
+
const lockPath = join36(ctx.repoRoot, "bun.lock");
|
|
109703
|
+
if (existsSync13(lockPath) && Bun.file(lockPath).size > 0)
|
|
109234
109704
|
staged.push("bun.lock");
|
|
109235
109705
|
await git2(ctx.repoRoot, ["add", ...staged]);
|
|
109236
109706
|
const shortNames = configs.map((c3) => c3.packageName.split("/").pop() ?? c3.packageName).join(" + ");
|
|
@@ -109244,7 +109714,13 @@ async function bumpAll(ctx, version3, options, output2) {
|
|
|
109244
109714
|
output2.write(`Tagged (trace): ${tag}`);
|
|
109245
109715
|
}
|
|
109246
109716
|
}
|
|
109247
|
-
await git2(ctx.repoRoot, [
|
|
109717
|
+
await git2(ctx.repoRoot, [
|
|
109718
|
+
"tag",
|
|
109719
|
+
"-a",
|
|
109720
|
+
aggregateTag,
|
|
109721
|
+
"-m",
|
|
109722
|
+
`${ctx.aggregate?.packageName ?? ctx.rootName} ${version3} \u2014 ${shortNames}`
|
|
109723
|
+
]);
|
|
109248
109724
|
output2.write(`Tagged (publish): ${aggregateTag}`);
|
|
109249
109725
|
if (!options.push) {
|
|
109250
109726
|
output2.write(`
|
|
@@ -109288,7 +109764,7 @@ async function dropAll(ctx, version3, options, output2) {
|
|
|
109288
109764
|
for (const config4 of ctx.allPackages) {
|
|
109289
109765
|
await dropTagsFor(ctx, config4, version3, options, output2);
|
|
109290
109766
|
}
|
|
109291
|
-
const aggregateTag =
|
|
109767
|
+
const aggregateTag = resolveAggregateTag(ctx, version3);
|
|
109292
109768
|
const localTags = new Set((await git2(ctx.repoRoot, ["tag", "-l"])).split(`
|
|
109293
109769
|
`).filter(Boolean));
|
|
109294
109770
|
if (localTags.has(aggregateTag)) {
|
|
@@ -109829,7 +110305,7 @@ import { createRequire } from "module";
|
|
|
109829
110305
|
var CLI_CONFIG = {
|
|
109830
110306
|
binaryName: "spur",
|
|
109831
110307
|
binaryLabel: "spur",
|
|
109832
|
-
binaryVersion: "0.3.
|
|
110308
|
+
binaryVersion: "0.3.84",
|
|
109833
110309
|
configDir: ".spur",
|
|
109834
110310
|
configFile: ".spur/config.yaml",
|
|
109835
110311
|
databaseFile: ".spur/spur.db"
|
|
@@ -110240,8 +110716,8 @@ init_src();
|
|
|
110240
110716
|
init_loader();
|
|
110241
110717
|
init_src2();
|
|
110242
110718
|
init_dist10();
|
|
110243
|
-
import { homedir as
|
|
110244
|
-
import { join as
|
|
110719
|
+
import { homedir as homedir10 } from "os";
|
|
110720
|
+
import { join as join37, resolve as resolve18 } from "path";
|
|
110245
110721
|
|
|
110246
110722
|
// src/config/scaffold-manifest.ts
|
|
110247
110723
|
var SCAFFOLD_MANIFEST = [
|
|
@@ -110270,8 +110746,8 @@ var SCAFFOLD_MANIFEST = [
|
|
|
110270
110746
|
];
|
|
110271
110747
|
|
|
110272
110748
|
// src/commands/init.ts
|
|
110273
|
-
var GLOBAL_CONFIG_DIR =
|
|
110274
|
-
var GLOBAL_RULES_DIR2 =
|
|
110749
|
+
var GLOBAL_CONFIG_DIR = join37(".config", "spur");
|
|
110750
|
+
var GLOBAL_RULES_DIR2 = join37(GLOBAL_CONFIG_DIR, "rules");
|
|
110275
110751
|
var GLOBAL_CONFIG_EXAMPLE = BUNDLED_GLOBAL_CONFIG;
|
|
110276
110752
|
var GLOBAL_CONFIG_FILE2 = "config.yaml";
|
|
110277
110753
|
var INDEXED_CONTEXT_MARKER = "## Indexed context";
|
|
@@ -110302,7 +110778,7 @@ If \`.spur/context/\` is absent, proceed normally. Never block work on its absen
|
|
|
110302
110778
|
`;
|
|
110303
110779
|
function globalRulesRoot(context4) {
|
|
110304
110780
|
const override = context4.env.SPUR_GLOBAL_RULES_DIR;
|
|
110305
|
-
return override !== undefined && override.length > 0 ?
|
|
110781
|
+
return override !== undefined && override.length > 0 ? resolve18(context4.cwd, override) : join37(homedir10(), GLOBAL_RULES_DIR2);
|
|
110306
110782
|
}
|
|
110307
110783
|
async function writeIfNew(context4, path9, content, force, result) {
|
|
110308
110784
|
if (!force && await context4.fs.exists(path9)) {
|
|
@@ -110319,18 +110795,18 @@ async function seedGlobalRules(context4) {
|
|
|
110319
110795
|
const target = globalRulesRoot(context4);
|
|
110320
110796
|
let written = 0;
|
|
110321
110797
|
for (const relPath of await listBundledRuleFiles()) {
|
|
110322
|
-
const destination =
|
|
110798
|
+
const destination = join37(target, relPath);
|
|
110323
110799
|
if (await context4.fs.exists(destination))
|
|
110324
110800
|
continue;
|
|
110325
|
-
await context4.fs.ensureDir(
|
|
110326
|
-
await context4.fs.writeFile(destination, await context4.fs.readFile(
|
|
110801
|
+
await context4.fs.ensureDir(join37(target, ...relPath.split("/").slice(0, -1)));
|
|
110802
|
+
await context4.fs.writeFile(destination, await context4.fs.readFile(join37(source, relPath)));
|
|
110327
110803
|
written += 1;
|
|
110328
110804
|
}
|
|
110329
110805
|
return written;
|
|
110330
110806
|
}
|
|
110331
110807
|
function resolveGlobalConfigDir(context4) {
|
|
110332
110808
|
const globalOverride = context4.env.SPUR_GLOBAL_RULES_DIR;
|
|
110333
|
-
return globalOverride !== undefined && globalOverride.length > 0 ?
|
|
110809
|
+
return globalOverride !== undefined && globalOverride.length > 0 ? resolve18(context4.cwd, globalOverride) : join37(homedir10(), GLOBAL_CONFIG_DIR);
|
|
110334
110810
|
}
|
|
110335
110811
|
async function seedGlobalConfig(context4) {
|
|
110336
110812
|
const source = bundledConfigRoot();
|
|
@@ -110341,15 +110817,15 @@ async function seedGlobalConfig(context4) {
|
|
|
110341
110817
|
for (const relPath of listBundledConfigFiles()) {
|
|
110342
110818
|
if (relPath === GLOBAL_CONFIG_EXAMPLE)
|
|
110343
110819
|
continue;
|
|
110344
|
-
const destination =
|
|
110820
|
+
const destination = join37(target, relPath);
|
|
110345
110821
|
if (await context4.fs.exists(destination))
|
|
110346
110822
|
continue;
|
|
110347
|
-
await context4.fs.ensureDir(
|
|
110348
|
-
await context4.fs.writeFile(destination, await context4.fs.readFile(
|
|
110823
|
+
await context4.fs.ensureDir(join37(target, ...relPath.split("/").slice(0, -1)));
|
|
110824
|
+
await context4.fs.writeFile(destination, await context4.fs.readFile(join37(source, relPath)));
|
|
110349
110825
|
written += 1;
|
|
110350
110826
|
}
|
|
110351
|
-
const examplePath =
|
|
110352
|
-
const globalConfigPath =
|
|
110827
|
+
const examplePath = join37(source, GLOBAL_CONFIG_EXAMPLE);
|
|
110828
|
+
const globalConfigPath = join37(target, GLOBAL_CONFIG_FILE2);
|
|
110353
110829
|
if (await context4.fs.exists(examplePath) && !await context4.fs.exists(globalConfigPath)) {
|
|
110354
110830
|
await context4.fs.ensureDir(target);
|
|
110355
110831
|
await context4.fs.writeFile(globalConfigPath, await context4.fs.readFile(examplePath));
|
|
@@ -110364,10 +110840,10 @@ function registerInitCommand(program2, context4, options = {}) {
|
|
|
110364
110840
|
const minimal = options2.minimal === true;
|
|
110365
110841
|
const adoptGlobal = options2.adoptGlobalConfig === true;
|
|
110366
110842
|
const projectName = options2.name ?? "default";
|
|
110367
|
-
const configPath =
|
|
110843
|
+
const configPath = join37(context4.cwd, CLI_CONFIG.configFile);
|
|
110368
110844
|
const result = { created: [], skipped: [] };
|
|
110369
110845
|
const globalConfigDir = resolveGlobalConfigDir(context4);
|
|
110370
|
-
const globalConfigPath =
|
|
110846
|
+
const globalConfigPath = join37(globalConfigDir, GLOBAL_CONFIG_FILE2);
|
|
110371
110847
|
const globalConfigExists = await context4.fs.exists(globalConfigPath);
|
|
110372
110848
|
let misplacedKeys = [];
|
|
110373
110849
|
if (globalConfigExists) {
|
|
@@ -110380,20 +110856,20 @@ function registerInitCommand(program2, context4, options = {}) {
|
|
|
110380
110856
|
const configRoot = bundledConfigRoot();
|
|
110381
110857
|
if (configRoot !== null && !minimal) {
|
|
110382
110858
|
for (const relPath of listBundledProjectSeedFiles()) {
|
|
110383
|
-
const sourcePath =
|
|
110859
|
+
const sourcePath = join37(configRoot, relPath);
|
|
110384
110860
|
if (!await context4.fs.exists(sourcePath))
|
|
110385
110861
|
continue;
|
|
110386
|
-
const targetPath =
|
|
110387
|
-
await context4.fs.ensureDir(
|
|
110862
|
+
const targetPath = join37(context4.cwd, CLI_CONFIG.configDir, relPath);
|
|
110863
|
+
await context4.fs.ensureDir(join37(targetPath, ".."));
|
|
110388
110864
|
await writeIfNew(context4, targetPath, await context4.fs.readFile(sourcePath), false, result);
|
|
110389
110865
|
}
|
|
110390
110866
|
for (const entry of SCAFFOLD_MANIFEST) {
|
|
110391
|
-
const sourcePath =
|
|
110867
|
+
const sourcePath = join37(configRoot, entry.source);
|
|
110392
110868
|
if (!await context4.fs.exists(sourcePath))
|
|
110393
110869
|
continue;
|
|
110394
|
-
const baseDir = entry.root === true ? context4.cwd :
|
|
110395
|
-
const targetPath =
|
|
110396
|
-
await context4.fs.ensureDir(
|
|
110870
|
+
const baseDir = entry.root === true ? context4.cwd : join37(context4.cwd, CLI_CONFIG.configDir);
|
|
110871
|
+
const targetPath = join37(baseDir, entry.target);
|
|
110872
|
+
await context4.fs.ensureDir(join37(targetPath, ".."));
|
|
110397
110873
|
const entryForce = false;
|
|
110398
110874
|
let body = await context4.fs.readFile(sourcePath);
|
|
110399
110875
|
if (entry.target === "AGENTS.md") {
|
|
@@ -110410,7 +110886,7 @@ function registerInitCommand(program2, context4, options = {}) {
|
|
|
110410
110886
|
const backupPath = `${globalConfigPath}.bak-${new Date().toISOString().replace(/[:.]/g, "-")}`;
|
|
110411
110887
|
await context4.fs.writeFile(backupPath, await context4.fs.readFile(globalConfigPath));
|
|
110412
110888
|
const bundledRoot = bundledConfigRoot();
|
|
110413
|
-
const examplePath = bundledRoot !== null ?
|
|
110889
|
+
const examplePath = bundledRoot !== null ? join37(bundledRoot, GLOBAL_CONFIG_EXAMPLE) : null;
|
|
110414
110890
|
if (examplePath !== null && await context4.fs.exists(examplePath)) {
|
|
110415
110891
|
await context4.fs.writeFile(globalConfigPath, await context4.fs.readFile(examplePath));
|
|
110416
110892
|
adoptedGlobal = true;
|
|
@@ -110480,13 +110956,13 @@ function registerInitCommand(program2, context4, options = {}) {
|
|
|
110480
110956
|
].join(`
|
|
110481
110957
|
`)}
|
|
110482
110958
|
`;
|
|
110483
|
-
await context4.fs.ensureDir(
|
|
110959
|
+
await context4.fs.ensureDir(join37(context4.cwd, CLI_CONFIG.configDir));
|
|
110484
110960
|
await context4.fs.writeFile(configPath, configYaml);
|
|
110485
110961
|
result.created.push(configPath);
|
|
110486
|
-
const agentsDir =
|
|
110962
|
+
const agentsDir = join37(context4.cwd, CLI_CONFIG.configDir, "agents");
|
|
110487
110963
|
await context4.fs.ensureDir(agentsDir);
|
|
110488
|
-
await writeIfNew(context4,
|
|
110489
|
-
const gitignorePath =
|
|
110964
|
+
await writeIfNew(context4, join37(agentsDir, ".gitkeep"), "", force, result);
|
|
110965
|
+
const gitignorePath = join37(context4.cwd, ".gitignore");
|
|
110490
110966
|
const contextEntry = ".spur/context/";
|
|
110491
110967
|
if (await context4.fs.exists(gitignorePath)) {
|
|
110492
110968
|
const existing = await context4.fs.readFile(gitignorePath);
|
|
@@ -110506,20 +110982,20 @@ ${contextEntry}
|
|
|
110506
110982
|
const configRoot = bundledConfigRoot();
|
|
110507
110983
|
if (configRoot !== null) {
|
|
110508
110984
|
for (const relPath of listBundledProjectSeedFiles()) {
|
|
110509
|
-
const sourcePath =
|
|
110985
|
+
const sourcePath = join37(configRoot, relPath);
|
|
110510
110986
|
if (!await context4.fs.exists(sourcePath))
|
|
110511
110987
|
continue;
|
|
110512
|
-
const targetPath =
|
|
110513
|
-
await context4.fs.ensureDir(
|
|
110988
|
+
const targetPath = join37(context4.cwd, CLI_CONFIG.configDir, relPath);
|
|
110989
|
+
await context4.fs.ensureDir(join37(targetPath, ".."));
|
|
110514
110990
|
await writeIfNew(context4, targetPath, await context4.fs.readFile(sourcePath), force, result);
|
|
110515
110991
|
}
|
|
110516
110992
|
for (const entry of SCAFFOLD_MANIFEST) {
|
|
110517
|
-
const sourcePath =
|
|
110993
|
+
const sourcePath = join37(configRoot, entry.source);
|
|
110518
110994
|
if (!await context4.fs.exists(sourcePath))
|
|
110519
110995
|
continue;
|
|
110520
|
-
const baseDir = entry.root === true ? context4.cwd :
|
|
110521
|
-
const targetPath =
|
|
110522
|
-
await context4.fs.ensureDir(
|
|
110996
|
+
const baseDir = entry.root === true ? context4.cwd : join37(context4.cwd, CLI_CONFIG.configDir);
|
|
110997
|
+
const targetPath = join37(baseDir, entry.target);
|
|
110998
|
+
await context4.fs.ensureDir(join37(targetPath, ".."));
|
|
110523
110999
|
const entryForce = entry.preserve === true ? false : force;
|
|
110524
111000
|
let body = await context4.fs.readFile(sourcePath);
|
|
110525
111001
|
if (entry.target === "AGENTS.md") {
|
|
@@ -110532,7 +111008,7 @@ ${contextEntry}
|
|
|
110532
111008
|
}
|
|
110533
111009
|
}
|
|
110534
111010
|
}
|
|
110535
|
-
const agentsMdPath =
|
|
111011
|
+
const agentsMdPath = join37(context4.cwd, "AGENTS.md");
|
|
110536
111012
|
if (await context4.fs.exists(agentsMdPath)) {
|
|
110537
111013
|
const existing = await context4.fs.readFile(agentsMdPath);
|
|
110538
111014
|
if (!existing.includes(INDEXED_CONTEXT_MARKER)) {
|
|
@@ -110573,11 +111049,11 @@ ${contextEntry}
|
|
|
110573
111049
|
// src/commands/maintain.ts
|
|
110574
111050
|
init_src();
|
|
110575
111051
|
init_src2();
|
|
110576
|
-
import { join as
|
|
111052
|
+
import { join as join38 } from "path";
|
|
110577
111053
|
function registerMaintainCommand(program2, context4, options = {}) {
|
|
110578
111054
|
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) => {
|
|
110579
111055
|
const db2 = await context4.getDb();
|
|
110580
|
-
const dbPath = context4.env.DATABASE_URL ??
|
|
111056
|
+
const dbPath = context4.env.DATABASE_URL ?? join38(context4.cwd, DEFAULT_DATABASE_URL);
|
|
110581
111057
|
const result = await maintainDatabase(db2, {
|
|
110582
111058
|
vacuum: options2.vacuum === true,
|
|
110583
111059
|
dbPath: dbPath.startsWith(":memory:") ? undefined : dbPath
|
|
@@ -110900,15 +111376,15 @@ async function runMessageWatch(svc, output2, options, runtime = {}) {
|
|
|
110900
111376
|
}
|
|
110901
111377
|
}
|
|
110902
111378
|
function defaultSleep(ms, signal) {
|
|
110903
|
-
return new Promise((
|
|
111379
|
+
return new Promise((resolve19) => {
|
|
110904
111380
|
if (signal?.aborted) {
|
|
110905
|
-
|
|
111381
|
+
resolve19();
|
|
110906
111382
|
return;
|
|
110907
111383
|
}
|
|
110908
|
-
const timer = setTimeout(
|
|
111384
|
+
const timer = setTimeout(resolve19, ms);
|
|
110909
111385
|
signal?.addEventListener("abort", () => {
|
|
110910
111386
|
clearTimeout(timer);
|
|
110911
|
-
|
|
111387
|
+
resolve19();
|
|
110912
111388
|
}, { once: true });
|
|
110913
111389
|
});
|
|
110914
111390
|
}
|
|
@@ -110935,15 +111411,15 @@ function parseTimeout2(raw) {
|
|
|
110935
111411
|
return n3;
|
|
110936
111412
|
}
|
|
110937
111413
|
function sendSleep(ms, signal) {
|
|
110938
|
-
return new Promise((
|
|
111414
|
+
return new Promise((resolve19) => {
|
|
110939
111415
|
if (signal?.aborted) {
|
|
110940
|
-
|
|
111416
|
+
resolve19();
|
|
110941
111417
|
return;
|
|
110942
111418
|
}
|
|
110943
|
-
const timer = setTimeout(
|
|
111419
|
+
const timer = setTimeout(resolve19, ms);
|
|
110944
111420
|
signal?.addEventListener("abort", () => {
|
|
110945
111421
|
clearTimeout(timer);
|
|
110946
|
-
|
|
111422
|
+
resolve19();
|
|
110947
111423
|
}, { once: true });
|
|
110948
111424
|
});
|
|
110949
111425
|
}
|
|
@@ -111001,10 +111477,10 @@ async function waitForPendingDrain(agentService, teamService, pin, timeoutMs, si
|
|
|
111001
111477
|
|
|
111002
111478
|
// src/commands/migrate.ts
|
|
111003
111479
|
init_src2();
|
|
111004
|
-
import { join as
|
|
111480
|
+
import { join as join39 } from "path";
|
|
111005
111481
|
function registerMigrateCommand(program2, context4, options = {}) {
|
|
111006
111482
|
program2.command("migrate", { hidden: options.hidden === true }).summary("apply CLI-owned schema migrations").option(...SHARED_OPTIONS.json).option(...SHARED_OPTIONS.jsonEnvelope).action(async (options2) => {
|
|
111007
|
-
const migrations = await loadSqlMigrations(
|
|
111483
|
+
const migrations = await loadSqlMigrations(join39(context4.cwd, "drizzle")).catch(() => {
|
|
111008
111484
|
return;
|
|
111009
111485
|
});
|
|
111010
111486
|
const applied = await applyCliMigrations(await context4.getDb(), migrations);
|
|
@@ -111017,16 +111493,16 @@ function registerMigrateCommand(program2, context4, options = {}) {
|
|
|
111017
111493
|
init_src2();
|
|
111018
111494
|
init_dist5();
|
|
111019
111495
|
await init_src3();
|
|
111020
|
-
import { basename as
|
|
111496
|
+
import { basename as basename13, dirname as dirname25, join as join40, resolve as resolve19 } from "path";
|
|
111021
111497
|
function registerProjectsCommand(program2, context4) {
|
|
111022
111498
|
const projectsCmd = program2.command("projects").summary("manage the Spur multi-project registry");
|
|
111023
111499
|
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) => {
|
|
111024
111500
|
try {
|
|
111025
|
-
const absolutePath =
|
|
111501
|
+
const absolutePath = resolve19(context4.cwd, pathArg);
|
|
111026
111502
|
if (!await context4.fs.exists(absolutePath)) {
|
|
111027
111503
|
throw new Error(`Directory does not exist: ${absolutePath}`);
|
|
111028
111504
|
}
|
|
111029
|
-
const name = options.name ??
|
|
111505
|
+
const name = options.name ?? basename13(absolutePath);
|
|
111030
111506
|
const registry3 = new ProjectRegistry;
|
|
111031
111507
|
const entry = await registry3.upsert({ path: absolutePath, name, port: 0 });
|
|
111032
111508
|
if (options.json) {
|
|
@@ -111075,8 +111551,8 @@ function registerProjectsCommand(program2, context4) {
|
|
|
111075
111551
|
running: p.port > 0 ? await isPortLive(p.port) : false
|
|
111076
111552
|
})));
|
|
111077
111553
|
const openProjectDb = async (projectPath) => {
|
|
111078
|
-
const url2 =
|
|
111079
|
-
await context4.fs.ensureDir(
|
|
111554
|
+
const url2 = join40(projectPath, ".spur", "spur.db");
|
|
111555
|
+
await context4.fs.ensureDir(dirname25(url2));
|
|
111080
111556
|
return createMigratedDb({ url: url2 });
|
|
111081
111557
|
};
|
|
111082
111558
|
const fleets = options.fleet ? await Promise.all(projects3.map(async (p) => {
|
|
@@ -111518,7 +111994,7 @@ function formatTraceDetail(detail) {
|
|
|
111518
111994
|
// src/commands/serve.ts
|
|
111519
111995
|
init_src();
|
|
111520
111996
|
import { statSync as statSync7 } from "fs";
|
|
111521
|
-
import { join as
|
|
111997
|
+
import { join as join44, resolve as resolve20 } from "path";
|
|
111522
111998
|
|
|
111523
111999
|
// ../server/src/index.ts
|
|
111524
112000
|
init_src();
|
|
@@ -111528,11 +112004,11 @@ init_src();
|
|
|
111528
112004
|
init_loader();
|
|
111529
112005
|
init_src2();
|
|
111530
112006
|
await init_src3();
|
|
111531
|
-
import { basename as
|
|
112007
|
+
import { basename as basename15, dirname as dirname27, isAbsolute as isAbsolute8, join as join43 } from "path";
|
|
111532
112008
|
init_dist5();
|
|
111533
112009
|
|
|
111534
112010
|
// ../server/src/bootstrap.ts
|
|
111535
|
-
import { join as
|
|
112011
|
+
import { join as join41 } from "path";
|
|
111536
112012
|
|
|
111537
112013
|
// ../../node_modules/.bun/radash@12.1.1/node_modules/radash/dist/esm/async.mjs
|
|
111538
112014
|
var guard = (func, shouldGuard) => {
|
|
@@ -111805,8 +112281,8 @@ class EventPublisher {
|
|
|
111805
112281
|
if (bufferedEvents.length > 0) {
|
|
111806
112282
|
return { done: false, value: bufferedEvents.shift() };
|
|
111807
112283
|
}
|
|
111808
|
-
return new Promise((
|
|
111809
|
-
pullResolvers.push([
|
|
112284
|
+
return new Promise((resolve20, reject) => {
|
|
112285
|
+
pullResolvers.push([resolve20, reject]);
|
|
111810
112286
|
});
|
|
111811
112287
|
}, async () => {
|
|
111812
112288
|
unsubscribe();
|
|
@@ -116926,6 +117402,55 @@ function mountMiddleware(app, appRt) {
|
|
|
116926
117402
|
}
|
|
116927
117403
|
}
|
|
116928
117404
|
|
|
117405
|
+
// ../server/src/modules/commands/index.ts
|
|
117406
|
+
init_src();
|
|
117407
|
+
await init_src3();
|
|
117408
|
+
var commandsModule = {
|
|
117409
|
+
name: "commands",
|
|
117410
|
+
mount(app, ctx) {
|
|
117411
|
+
const projectRoot = ctx?.cwd ?? process.cwd();
|
|
117412
|
+
const filePath = getSlashCommandsFilePath();
|
|
117413
|
+
const service = new SlashCommandsService({ filePath, projectRoot });
|
|
117414
|
+
let cachedCommands = [];
|
|
117415
|
+
try {
|
|
117416
|
+
cachedCommands = service.getCommands();
|
|
117417
|
+
} catch {}
|
|
117418
|
+
const handleList = (c3) => {
|
|
117419
|
+
const reload = c3.req.query("reload") === "true";
|
|
117420
|
+
if (reload || cachedCommands.length === 0) {
|
|
117421
|
+
try {
|
|
117422
|
+
cachedCommands = service.getCommands(reload);
|
|
117423
|
+
} catch {}
|
|
117424
|
+
}
|
|
117425
|
+
return c3.json({
|
|
117426
|
+
commands: cachedCommands,
|
|
117427
|
+
count: cachedCommands.length,
|
|
117428
|
+
filePath: service.getFilePath()
|
|
117429
|
+
});
|
|
117430
|
+
};
|
|
117431
|
+
app.get("/api/commands", handleList);
|
|
117432
|
+
app.get("/api/agent/commands", handleList);
|
|
117433
|
+
app.post("/api/commands/generate", (c3) => {
|
|
117434
|
+
try {
|
|
117435
|
+
const result = service.generate();
|
|
117436
|
+
cachedCommands = result.commands;
|
|
117437
|
+
return c3.json({
|
|
117438
|
+
ok: true,
|
|
117439
|
+
count: result.count,
|
|
117440
|
+
filePath: result.path,
|
|
117441
|
+
commands: result.commands
|
|
117442
|
+
});
|
|
117443
|
+
} catch (err) {
|
|
117444
|
+
return c3.json({
|
|
117445
|
+
ok: false,
|
|
117446
|
+
error: err instanceof Error ? err.message : String(err),
|
|
117447
|
+
filePath: service.getFilePath()
|
|
117448
|
+
}, 500);
|
|
117449
|
+
}
|
|
117450
|
+
});
|
|
117451
|
+
}
|
|
117452
|
+
};
|
|
117453
|
+
|
|
116929
117454
|
// ../server/src/modules/sse/stream-helpers.ts
|
|
116930
117455
|
function sendSseKeepalive(closed, controller, encoder) {
|
|
116931
117456
|
if (closed.current)
|
|
@@ -118234,7 +118759,7 @@ var featureModule = {
|
|
|
118234
118759
|
// ../server/src/modules/health/index.ts
|
|
118235
118760
|
init_src2();
|
|
118236
118761
|
await init_src3();
|
|
118237
|
-
import { basename as
|
|
118762
|
+
import { basename as basename14 } from "path";
|
|
118238
118763
|
var startedAt = Date.now();
|
|
118239
118764
|
var OPERATOR_AGENT_ID = "board-operator";
|
|
118240
118765
|
var healthModule = {
|
|
@@ -118261,7 +118786,7 @@ var healthModule = {
|
|
|
118261
118786
|
return c3.json({ status: "error", db: "unreachable" }, 503);
|
|
118262
118787
|
});
|
|
118263
118788
|
app.get("/api/project", (c3) => {
|
|
118264
|
-
return c3.json({ name: ctx ?
|
|
118789
|
+
return c3.json({ name: ctx ? basename14(ctx.cwd) : null, path: ctx ? normalizeProjectPath(ctx.cwd) : null });
|
|
118265
118790
|
});
|
|
118266
118791
|
app.get("/api/project/fleet", async (c3) => {
|
|
118267
118792
|
if (!ctx) {
|
|
@@ -119341,7 +119866,8 @@ var builtins = [
|
|
|
119341
119866
|
processesModule,
|
|
119342
119867
|
observabilityModule,
|
|
119343
119868
|
runsModule,
|
|
119344
|
-
historyModule
|
|
119869
|
+
historyModule,
|
|
119870
|
+
commandsModule
|
|
119345
119871
|
];
|
|
119346
119872
|
function registerModules(app, ctx, modules = builtins) {
|
|
119347
119873
|
for (const mod of modules) {
|
|
@@ -121359,7 +121885,7 @@ function createApp(appRt, opts) {
|
|
|
121359
121885
|
app.use("*", async (c3, next) => {
|
|
121360
121886
|
const pathname = c3.req.path === "/" ? "/index.html" : c3.req.path;
|
|
121361
121887
|
try {
|
|
121362
|
-
const file2 = Bun.file(
|
|
121888
|
+
const file2 = Bun.file(join41(webDistPath, pathname));
|
|
121363
121889
|
if (await file2.exists()) {
|
|
121364
121890
|
const headers = new Headers({ "content-type": file2.type });
|
|
121365
121891
|
return new Response(file2.stream(), { headers });
|
|
@@ -121372,7 +121898,7 @@ function createApp(appRt, opts) {
|
|
|
121372
121898
|
return c3.json({ error: "Not Found" }, 404);
|
|
121373
121899
|
}
|
|
121374
121900
|
try {
|
|
121375
|
-
const indexFile = Bun.file(
|
|
121901
|
+
const indexFile = Bun.file(join41(webDistPath, "index.html"));
|
|
121376
121902
|
if (await indexFile.exists()) {
|
|
121377
121903
|
return new Response(indexFile.stream(), {
|
|
121378
121904
|
headers: { "content-type": "text/html; charset=utf-8" }
|
|
@@ -121394,7 +121920,7 @@ init_loader();
|
|
|
121394
121920
|
init_src2();
|
|
121395
121921
|
init_dist5();
|
|
121396
121922
|
await init_src3();
|
|
121397
|
-
import { dirname as
|
|
121923
|
+
import { dirname as dirname26, join as join42 } from "path";
|
|
121398
121924
|
var NOOP_OUTPUT = { write: (_s) => {}, error: (_s) => {} };
|
|
121399
121925
|
|
|
121400
121926
|
class LazyPlanningEventEmitter {
|
|
@@ -121430,7 +121956,7 @@ class NotConfiguredError extends Error {
|
|
|
121430
121956
|
function createServerContext(appRt, options) {
|
|
121431
121957
|
const cwd = options.cwd;
|
|
121432
121958
|
const fs3 = options.fs;
|
|
121433
|
-
const dbUrl = options.dbUrl ??
|
|
121959
|
+
const dbUrl = options.dbUrl ?? join42(cwd, DEFAULT_DATABASE_URL);
|
|
121434
121960
|
const eventsBus = options.eventsBus ?? appRt.events;
|
|
121435
121961
|
const jobQueueEnabled = options.jobQueueEnabled ?? false;
|
|
121436
121962
|
const eventProjectContext = systemEventProjectContext(cwd);
|
|
@@ -121467,7 +121993,7 @@ function createServerContext(appRt, options) {
|
|
|
121467
121993
|
if (!dbPromise) {
|
|
121468
121994
|
const created = (async () => {
|
|
121469
121995
|
if (dbUrl !== IN_MEMORY_DATABASE_URL) {
|
|
121470
|
-
await fs3.ensureDir(
|
|
121996
|
+
await fs3.ensureDir(dirname26(dbUrl));
|
|
121471
121997
|
}
|
|
121472
121998
|
return createMigratedDbViaRuntime({ url: dbUrl });
|
|
121473
121999
|
})();
|
|
@@ -121735,7 +122261,7 @@ async function loadServerSectionMatrix(projectRoot) {
|
|
|
121735
122261
|
}
|
|
121736
122262
|
const root = bundledConfigRoot();
|
|
121737
122263
|
if (root !== null) {
|
|
121738
|
-
const matrixPath =
|
|
122264
|
+
const matrixPath = join43(root, "tasks", "section-matrix.yaml");
|
|
121739
122265
|
if (await nodeFs.exists(matrixPath)) {
|
|
121740
122266
|
return await loadStructuredSpurConfig(matrixPath, {
|
|
121741
122267
|
validateJsonSchema: false
|
|
@@ -121744,7 +122270,7 @@ async function loadServerSectionMatrix(projectRoot) {
|
|
|
121744
122270
|
}
|
|
121745
122271
|
throw new Error(`no canonical section-matrix found for task creation (F92 R1); tried:
|
|
121746
122272
|
` + ` - ${localPath}
|
|
121747
|
-
` + (root !== null ? ` - ${
|
|
122273
|
+
` + (root !== null ? ` - ${join43(root, "tasks", "section-matrix.yaml")}
|
|
121748
122274
|
` : "") + "copy/generate section-matrix.yaml from the canonical build-time matrix asset (repo `config` `tasks` tree) into one of those paths");
|
|
121749
122275
|
}
|
|
121750
122276
|
var SYSTEM_EVENTS_PRUNE_JOB = "system-events-prune";
|
|
@@ -121813,41 +122339,58 @@ function registerSchedulerEntries(scheduler3, ctx, jobs = [], options = {}) {
|
|
|
121813
122339
|
const sweepThresholdMs = jobPolicy === null ? null : jobPolicy + resolveKillGraceMs3(env);
|
|
121814
122340
|
register(schedule, `${SCHEDULER_CUSTOM_JOB}:${job.name}`, async () => {
|
|
121815
122341
|
const queue = await ctx.jobQueue();
|
|
122342
|
+
const enqueueFresh = () => queue.enqueue(SCHEDULER_CUSTOM_JOB, {
|
|
122343
|
+
name: job.name,
|
|
122344
|
+
command: job.command,
|
|
122345
|
+
...historyProducerExclusiveKeyFor(job.command) !== undefined && {
|
|
122346
|
+
exclusiveKey: historyProducerExclusiveKeyFor(job.command)
|
|
122347
|
+
}
|
|
122348
|
+
}, { maxRetries: 1 });
|
|
122349
|
+
try {
|
|
122350
|
+
await enqueueFresh();
|
|
122351
|
+
return;
|
|
122352
|
+
} catch (error51) {
|
|
122353
|
+
if (!isSchedulerCustomActiveConflict(error51))
|
|
122354
|
+
throw error51;
|
|
122355
|
+
}
|
|
121816
122356
|
const db2 = await ctx.getDb();
|
|
121817
122357
|
const active = await findActiveSchedulerCustomJob(db2, job.name);
|
|
121818
|
-
|
|
121819
|
-
|
|
121820
|
-
|
|
121821
|
-
|
|
121822
|
-
|
|
121823
|
-
|
|
121824
|
-
|
|
122358
|
+
const now3 = Date.now();
|
|
122359
|
+
const stale = active !== undefined && sweepThresholdMs !== null && active.status === "processing" && now3 - (active.processingAt ?? active.updatedAt) > sweepThresholdMs;
|
|
122360
|
+
if (active !== undefined && stale && sweepThresholdMs !== null) {
|
|
122361
|
+
const reason = `age-sweep: processing exceeded ${sweepThresholdMs}ms`;
|
|
122362
|
+
const swept = await failStaleSchedulerCustomJob(db2, active.id, now3, reason);
|
|
122363
|
+
if (swept) {
|
|
122364
|
+
ctx.eventBus().emit("scheduler.job.executed", {
|
|
122365
|
+
name: `${SCHEDULER_CUSTOM_JOB}:${job.name}`,
|
|
122366
|
+
durationMs: 0,
|
|
122367
|
+
severity: "info",
|
|
122368
|
+
swept: true,
|
|
122369
|
+
reason
|
|
122370
|
+
});
|
|
122371
|
+
try {
|
|
122372
|
+
await enqueueFresh();
|
|
122373
|
+
} catch (error51) {
|
|
122374
|
+
if (!isSchedulerCustomActiveConflict(error51))
|
|
122375
|
+
throw error51;
|
|
121825
122376
|
ctx.eventBus().emit("scheduler.job.executed", {
|
|
121826
122377
|
name: `${SCHEDULER_CUSTOM_JOB}:${job.name}`,
|
|
121827
122378
|
durationMs: 0,
|
|
121828
122379
|
severity: "info",
|
|
121829
|
-
|
|
121830
|
-
reason
|
|
122380
|
+
skipped: true,
|
|
122381
|
+
reason: "post-sweep enqueue lost to a concurrent tick"
|
|
121831
122382
|
});
|
|
121832
122383
|
}
|
|
121833
|
-
} else {
|
|
121834
|
-
ctx.eventBus().emit("scheduler.job.executed", {
|
|
121835
|
-
name: `${SCHEDULER_CUSTOM_JOB}:${job.name}`,
|
|
121836
|
-
durationMs: 0,
|
|
121837
|
-
severity: "info",
|
|
121838
|
-
skipped: true,
|
|
121839
|
-
reason: `active job ${active.id} already exists`
|
|
121840
|
-
});
|
|
121841
122384
|
return;
|
|
121842
122385
|
}
|
|
121843
122386
|
}
|
|
121844
|
-
|
|
121845
|
-
name: job.name
|
|
121846
|
-
|
|
121847
|
-
|
|
121848
|
-
|
|
121849
|
-
}
|
|
121850
|
-
}
|
|
122387
|
+
ctx.eventBus().emit("scheduler.job.executed", {
|
|
122388
|
+
name: `${SCHEDULER_CUSTOM_JOB}:${job.name}`,
|
|
122389
|
+
durationMs: 0,
|
|
122390
|
+
severity: "info",
|
|
122391
|
+
skipped: true,
|
|
122392
|
+
reason: `active job ${active?.id ?? "unknown"} already exists`
|
|
122393
|
+
});
|
|
121851
122394
|
});
|
|
121852
122395
|
registrations.push({
|
|
121853
122396
|
name: job.name,
|
|
@@ -121962,15 +122505,15 @@ async function handleFeatureActionJob(ctx, env, payload) {
|
|
|
121962
122505
|
await runFeatureActionJob(ctx, env, payload);
|
|
121963
122506
|
}
|
|
121964
122507
|
async function resolveWebDistPath(configuredPath, projectRoot = process.cwd()) {
|
|
121965
|
-
const candidates = configuredPath && configuredPath.trim() !== "" ? [isAbsolute8(configuredPath) ? configuredPath :
|
|
121966
|
-
|
|
121967
|
-
|
|
121968
|
-
|
|
121969
|
-
|
|
121970
|
-
|
|
122508
|
+
const candidates = configuredPath && configuredPath.trim() !== "" ? [isAbsolute8(configuredPath) ? configuredPath : join43(projectRoot, configuredPath)] : [
|
|
122509
|
+
join43(projectRoot, "dist/web"),
|
|
122510
|
+
join43(import.meta.dir, "web"),
|
|
122511
|
+
join43(dirname27(process.execPath), "web"),
|
|
122512
|
+
join43(dirname27(process.execPath), "../web"),
|
|
122513
|
+
join43(import.meta.dir, "../../../dist/web")
|
|
121971
122514
|
];
|
|
121972
122515
|
for (const candidate of candidates) {
|
|
121973
|
-
if (await Bun.file(
|
|
122516
|
+
if (await Bun.file(join43(candidate, "index.html")).exists()) {
|
|
121974
122517
|
return candidate;
|
|
121975
122518
|
}
|
|
121976
122519
|
}
|
|
@@ -121987,7 +122530,7 @@ async function startServer(options, deps = defaultDeps) {
|
|
|
121987
122530
|
async start(appRt) {
|
|
121988
122531
|
const fs3 = deps.createNodeFileSystem(projectRoot);
|
|
121989
122532
|
if (options.dbUrl && options.dbUrl !== IN_MEMORY_DATABASE_URL) {
|
|
121990
|
-
await fs3.ensureDir(
|
|
122533
|
+
await fs3.ensureDir(dirname27(options.dbUrl));
|
|
121991
122534
|
}
|
|
121992
122535
|
const scheduler3 = appRt.config.scheduler.enabled ? appRt.scheduler : undefined;
|
|
121993
122536
|
const webDistPath = await resolveWebDistPath(options.webDistPath, projectRoot);
|
|
@@ -122126,7 +122669,16 @@ async function startServer(options, deps = defaultDeps) {
|
|
|
122126
122669
|
return handleSchedulerCustomJob({
|
|
122127
122670
|
cwd: ctx.cwd,
|
|
122128
122671
|
executor: childExecutor,
|
|
122129
|
-
resolveTimeoutMs: (name) => resolveSchedulerJobTimeoutMs(name, env, schedulerCustomTimeoutMs)
|
|
122672
|
+
resolveTimeoutMs: (name) => resolveSchedulerJobTimeoutMs(name, env, schedulerCustomTimeoutMs),
|
|
122673
|
+
onDuplicate: (name) => {
|
|
122674
|
+
ctx.eventBus().emit("scheduler.job.executed", {
|
|
122675
|
+
name: `${SCHEDULER_CUSTOM_JOB}:${name}`,
|
|
122676
|
+
durationMs: 0,
|
|
122677
|
+
severity: "info",
|
|
122678
|
+
skipped: true,
|
|
122679
|
+
reason: `duplicate execution suppressed; ${name} is already running in this process`
|
|
122680
|
+
});
|
|
122681
|
+
}
|
|
122130
122682
|
}, job);
|
|
122131
122683
|
});
|
|
122132
122684
|
const unlimitedJobNames = appRt.config.scheduler.jobs.filter((job) => resolveSchedulerJobTimeoutMs(job.name, env, schedulerCustomTimeoutMs) === null).map((job) => job.name);
|
|
@@ -122157,7 +122709,7 @@ async function startServer(options, deps = defaultDeps) {
|
|
|
122157
122709
|
});
|
|
122158
122710
|
const projectRegistry = new ProjectRegistry;
|
|
122159
122711
|
const projectCwd = projectRoot;
|
|
122160
|
-
const projectName =
|
|
122712
|
+
const projectName = basename15(projectCwd);
|
|
122161
122713
|
try {
|
|
122162
122714
|
const existingProject = await projectRegistry.getByPath(projectCwd);
|
|
122163
122715
|
await projectRegistry.upsert({
|
|
@@ -122277,10 +122829,10 @@ var SQLITE_BUSY_MESSAGE_CONSTANTS = Object.freeze({
|
|
|
122277
122829
|
|
|
122278
122830
|
// src/commands/serve.ts
|
|
122279
122831
|
function resolveServeDbUrl(cwd, env, configuredUrl) {
|
|
122280
|
-
return env.DATABASE_URL === undefined ?
|
|
122832
|
+
return env.DATABASE_URL === undefined ? join44(cwd, DEFAULT_DATABASE_URL) : configuredUrl;
|
|
122281
122833
|
}
|
|
122282
122834
|
function resolveServeCwd(cwd) {
|
|
122283
|
-
const resolved =
|
|
122835
|
+
const resolved = resolve20(cwd);
|
|
122284
122836
|
let isDirectory = false;
|
|
122285
122837
|
try {
|
|
122286
122838
|
isDirectory = statSync7(resolved).isDirectory();
|
|
@@ -122333,7 +122885,7 @@ function registerServeCommand(program2, context4, options = {}) {
|
|
|
122333
122885
|
}
|
|
122334
122886
|
|
|
122335
122887
|
// src/commands/status.ts
|
|
122336
|
-
import { join as
|
|
122888
|
+
import { join as join45 } from "path";
|
|
122337
122889
|
|
|
122338
122890
|
// src/git-context.ts
|
|
122339
122891
|
init_dist5();
|
|
@@ -122381,8 +122933,8 @@ function registerStatusCommand(program2, context4, options = {}) {
|
|
|
122381
122933
|
}
|
|
122382
122934
|
async function runStatusCore(path9, options, context4) {
|
|
122383
122935
|
const [packageJsonExists, spurConfigExists, git3, agentSpecs] = await Promise.all([
|
|
122384
|
-
context4.fs.exists(
|
|
122385
|
-
context4.fs.exists(
|
|
122936
|
+
context4.fs.exists(join45(context4.cwd, "package.json")),
|
|
122937
|
+
context4.fs.exists(join45(context4.cwd, ".spur", "config.yaml")),
|
|
122386
122938
|
gitContext(context4.cwd),
|
|
122387
122939
|
listAgentSpecIds(context4)
|
|
122388
122940
|
]);
|
|
@@ -122410,14 +122962,14 @@ async function runStatusCore(path9, options, context4) {
|
|
|
122410
122962
|
return status.ok ? 0 : 1;
|
|
122411
122963
|
}
|
|
122412
122964
|
async function listAgentSpecIds(context4) {
|
|
122413
|
-
const dir =
|
|
122965
|
+
const dir = join45(context4.cwd, ".spur", "agents");
|
|
122414
122966
|
if (!await context4.fs.exists(dir))
|
|
122415
122967
|
return [];
|
|
122416
122968
|
const entries = await context4.fs.readDir(dir);
|
|
122417
122969
|
return entries.filter((entry) => entry.endsWith(".yaml") || entry.endsWith(".yml")).map((entry) => entry.replace(/\.ya?ml$/, "")).sort();
|
|
122418
122970
|
}
|
|
122419
122971
|
async function readTargetStatus(context4, targetPath) {
|
|
122420
|
-
const resolved =
|
|
122972
|
+
const resolved = join45(context4.cwd, targetPath);
|
|
122421
122973
|
const stat2 = await context4.fs.stat(resolved);
|
|
122422
122974
|
if (stat2 === null)
|
|
122423
122975
|
throw new CommandError(`status failed: path does not exist at ${resolved}`);
|
|
@@ -122430,7 +122982,7 @@ init_dist7();
|
|
|
122430
122982
|
init_dist5();
|
|
122431
122983
|
await init_src3();
|
|
122432
122984
|
import { closeSync as closeSync5, fstatSync as fstatSync3, openSync as openSync5, readSync as readSync3 } from "fs";
|
|
122433
|
-
import { join as
|
|
122985
|
+
import { join as join46 } from "path";
|
|
122434
122986
|
import { createInterface } from "readline";
|
|
122435
122987
|
import { setTimeout as sleep5 } from "timers/promises";
|
|
122436
122988
|
|
|
@@ -122609,9 +123161,9 @@ async function writeWorkflowPlanArtifact(cwd, runId, resolved, secretValues) {
|
|
|
122609
123161
|
}))
|
|
122610
123162
|
});
|
|
122611
123163
|
const { mkdir, writeFile: writeFile2 } = await import("fs/promises");
|
|
122612
|
-
const artifactsDir =
|
|
123164
|
+
const artifactsDir = join46(cwd, ".spur", "run");
|
|
122613
123165
|
await mkdir(artifactsDir, { recursive: true });
|
|
122614
|
-
const artifactPath =
|
|
123166
|
+
const artifactPath = join46(artifactsDir, `${runId}-workflow-plan.json`);
|
|
122615
123167
|
await writeFile2(artifactPath, payload);
|
|
122616
123168
|
return artifactPath;
|
|
122617
123169
|
}
|
|
@@ -122896,7 +123448,7 @@ Monitor with: spur workflow trace ${runId2} --follow`);
|
|
|
122896
123448
|
}
|
|
122897
123449
|
const runLog = options.log === false ? undefined : new WorkflowRunLogSink({
|
|
122898
123450
|
bus,
|
|
122899
|
-
dir:
|
|
123451
|
+
dir: join46(context4.cwd, ".spur", "run"),
|
|
122900
123452
|
runId,
|
|
122901
123453
|
...planPreview !== undefined ? { planPreview } : {},
|
|
122902
123454
|
...resolveOutputLogConfig(context4.spurConfig ?? null)
|
|
@@ -123434,7 +123986,7 @@ function readRunLogChunk(logPath, offset) {
|
|
|
123434
123986
|
}
|
|
123435
123987
|
}
|
|
123436
123988
|
async function followRunLog(service, runId, dir, pollMs, write, wait = (ms) => sleep5(ms)) {
|
|
123437
|
-
const logPath =
|
|
123989
|
+
const logPath = join46(dir, ".spur", "run", `${runId}.log`);
|
|
123438
123990
|
let offset = 0;
|
|
123439
123991
|
let everRead = false;
|
|
123440
123992
|
while (true) {
|
|
@@ -123497,7 +124049,7 @@ init_src();
|
|
|
123497
124049
|
init_src2();
|
|
123498
124050
|
init_dist5();
|
|
123499
124051
|
await init_src3();
|
|
123500
|
-
import { dirname as
|
|
124052
|
+
import { dirname as dirname28, join as join47, resolve as resolve21 } from "path";
|
|
123501
124053
|
import { isatty as isatty4 } from "tty";
|
|
123502
124054
|
|
|
123503
124055
|
// ../../node_modules/.bun/@clack+core@1.4.1/node_modules/@clack/core/dist/index.mjs
|
|
@@ -124881,7 +125433,7 @@ class DefaultHitlResponder {
|
|
|
124881
125433
|
// src/context.ts
|
|
124882
125434
|
await init_src3();
|
|
124883
125435
|
function createCliContext(options) {
|
|
124884
|
-
const cwd =
|
|
125436
|
+
const cwd = resolve21(options.cwd ?? process.cwd());
|
|
124885
125437
|
const env = options.env ?? process.env;
|
|
124886
125438
|
const fs3 = createNodeFileSystem3(cwd);
|
|
124887
125439
|
const agentConfig = options.agentConfig ?? options.spurConfig?.agent;
|
|
@@ -124934,10 +125486,10 @@ function createCliContext(options) {
|
|
|
124934
125486
|
function noopSetExitCode(_code) {}
|
|
124935
125487
|
async function createMigratedDbAdapter(cwd = process.cwd(), env = process.env, dbUrl) {
|
|
124936
125488
|
const config4 = buildConfigFromEnv(env);
|
|
124937
|
-
const configuredUrl = env.DATABASE_URL === undefined ?
|
|
125489
|
+
const configuredUrl = env.DATABASE_URL === undefined ? join47(cwd, DEFAULT_DATABASE_URL) : config4.database.url;
|
|
124938
125490
|
const url2 = dbUrl ?? configuredUrl;
|
|
124939
125491
|
if (url2 !== IN_MEMORY_DATABASE_URL) {
|
|
124940
|
-
await createNodeFileSystem3().ensureDir(
|
|
125492
|
+
await createNodeFileSystem3().ensureDir(dirname28(url2));
|
|
124941
125493
|
}
|
|
124942
125494
|
return createMigratedDb({ url: url2 });
|
|
124943
125495
|
}
|