pipe-kan 0.31.0 → 0.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/pipe-kan.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/server.ts
|
|
4
|
-
import { readFileSync as
|
|
4
|
+
import { readFileSync as readFileSync7, writeSync } from "node:fs";
|
|
5
5
|
import { createServer } from "node:http";
|
|
6
6
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
7
|
-
import { join as
|
|
7
|
+
import { join as join11 } from "node:path";
|
|
8
8
|
|
|
9
9
|
// src/app.ts
|
|
10
10
|
import { readFileSync as readFileSync2 } from "node:fs";
|
|
@@ -399,8 +399,16 @@ function parseFlags(flags) {
|
|
|
399
399
|
statusEq.push(status);
|
|
400
400
|
continue;
|
|
401
401
|
}
|
|
402
|
-
if (token === "--projects") {
|
|
403
|
-
|
|
402
|
+
if (token === "--projects" || token.startsWith("--projects=")) {
|
|
403
|
+
const parts = [];
|
|
404
|
+
if (token.startsWith("--projects=")) {
|
|
405
|
+
parts.push(token.slice("--projects=".length));
|
|
406
|
+
}
|
|
407
|
+
while (i + 1 < list.length && !list[i + 1].startsWith("-")) {
|
|
408
|
+
i++;
|
|
409
|
+
parts.push(list[i]);
|
|
410
|
+
}
|
|
411
|
+
projectsValue = parts.join(",");
|
|
404
412
|
continue;
|
|
405
413
|
}
|
|
406
414
|
if (token === "-q" || token === "--jql") {
|
|
@@ -2272,6 +2280,123 @@ async function refreshFromJira(app, kind, opts = {}) {
|
|
|
2272
2280
|
await app.refresh();
|
|
2273
2281
|
}
|
|
2274
2282
|
|
|
2283
|
+
// src/factory-ledger.ts
|
|
2284
|
+
import { existsSync as existsSync2, readFileSync as readFileSync3, statSync } from "node:fs";
|
|
2285
|
+
import { homedir as homedir3 } from "node:os";
|
|
2286
|
+
import { join as join4 } from "node:path";
|
|
2287
|
+
var DEFAULT_LEDGER_PATH = "~/code/software-factory/asf/runtime/factory-coordinator/jobs.jsonl";
|
|
2288
|
+
function resolveLedgerPath(env = process.env) {
|
|
2289
|
+
const raw = env.FACTORY_LEDGER_PATH ?? DEFAULT_LEDGER_PATH;
|
|
2290
|
+
return raw.startsWith("~/") ? join4(homedir3(), raw.slice(2)) : raw;
|
|
2291
|
+
}
|
|
2292
|
+
function parseJobsJsonl(text) {
|
|
2293
|
+
const events = [];
|
|
2294
|
+
for (const line of text.split(`
|
|
2295
|
+
`)) {
|
|
2296
|
+
const trimmed = line.trim();
|
|
2297
|
+
if (!trimmed)
|
|
2298
|
+
continue;
|
|
2299
|
+
try {
|
|
2300
|
+
events.push(JSON.parse(trimmed));
|
|
2301
|
+
} catch {}
|
|
2302
|
+
}
|
|
2303
|
+
return events;
|
|
2304
|
+
}
|
|
2305
|
+
function lc(...parts) {
|
|
2306
|
+
return parts.filter(Boolean).join(" ").toLowerCase();
|
|
2307
|
+
}
|
|
2308
|
+
function deriveStageTag(events) {
|
|
2309
|
+
if (!events.length)
|
|
2310
|
+
return null;
|
|
2311
|
+
const last = events[events.length - 1];
|
|
2312
|
+
if (last.status === "done" || last.status === "cancelled")
|
|
2313
|
+
return null;
|
|
2314
|
+
const text = lc(last.detail, last.summary, last.target_agent, last.from_bot);
|
|
2315
|
+
const fromBot = lc(last.from_bot);
|
|
2316
|
+
const targetAgent = lc(last.target_agent);
|
|
2317
|
+
const detail = lc(last.detail);
|
|
2318
|
+
if (last.status === "blocked" || /needs.{0,4}judgment|green.{0,4}light|await.{0,4}ship|awaiting.{0,4}ship|needs.{0,4}input|waiting.{0,4}operator|checks.{0,4}pass|verify.{0,4}pass|human.{0,4}gate/.test(text)) {
|
|
2319
|
+
return "human-wait";
|
|
2320
|
+
}
|
|
2321
|
+
if (last.status === "queued" || last.status === "routed" || /grokbot.{0,4}coordinator|intake.{0,4}switchboard/.test(fromBot) || /routing|switchboard/.test(text)) {
|
|
2322
|
+
return "coordinator";
|
|
2323
|
+
}
|
|
2324
|
+
if (/planner|plan_|stage_1a/.test(targetAgent) || /planner|to.{0,2}spec|to.{0,2}tickets|grill.{0,4}with.{0,4}docs|stage_1/.test(text)) {
|
|
2325
|
+
return "planning";
|
|
2326
|
+
}
|
|
2327
|
+
if (/desk=/.test(detail) || /preflight|research.{0,4}gate|research.{0,4}dossier/.test(text)) {
|
|
2328
|
+
return "desk";
|
|
2329
|
+
}
|
|
2330
|
+
if (/maker|implement|referee|omp|stage_2/.test(targetAgent) || /\bmaker\b|implement|referee|stage_2|pr.{0,4}open|pull.{0,4}request/.test(text)) {
|
|
2331
|
+
return "maker";
|
|
2332
|
+
}
|
|
2333
|
+
return "coordinator";
|
|
2334
|
+
}
|
|
2335
|
+
function extractPrUrl(detail) {
|
|
2336
|
+
if (!detail)
|
|
2337
|
+
return;
|
|
2338
|
+
const m = /https:\/\/github\.com\/[^\s"']+\/pull\/\d+/.exec(detail);
|
|
2339
|
+
return m?.[0];
|
|
2340
|
+
}
|
|
2341
|
+
function groupByLatest(events) {
|
|
2342
|
+
const map = new Map;
|
|
2343
|
+
for (const ev of events) {
|
|
2344
|
+
if (!ev.id)
|
|
2345
|
+
continue;
|
|
2346
|
+
const list = map.get(ev.id);
|
|
2347
|
+
if (list) {
|
|
2348
|
+
list.push(ev);
|
|
2349
|
+
} else {
|
|
2350
|
+
map.set(ev.id, [ev]);
|
|
2351
|
+
}
|
|
2352
|
+
}
|
|
2353
|
+
return map;
|
|
2354
|
+
}
|
|
2355
|
+
function jobFromEvents(id, events) {
|
|
2356
|
+
const last = events[events.length - 1];
|
|
2357
|
+
const pullRequestUrl = events.map((e) => e.pull_request_url ?? extractPrUrl(e.detail)).find(Boolean);
|
|
2358
|
+
return {
|
|
2359
|
+
id,
|
|
2360
|
+
summary: last.summary ?? "",
|
|
2361
|
+
status: last.status ?? "",
|
|
2362
|
+
stageTag: deriveStageTag(events),
|
|
2363
|
+
latestTs: last.ts,
|
|
2364
|
+
...pullRequestUrl ? { pullRequestUrl } : {},
|
|
2365
|
+
...last.plane_project ? { planeProject: last.plane_project } : {},
|
|
2366
|
+
...last.detail ? { detail: last.detail } : {},
|
|
2367
|
+
...last.session ? { session: last.session } : {},
|
|
2368
|
+
...last.from_bot ? { fromBot: last.from_bot } : {}
|
|
2369
|
+
};
|
|
2370
|
+
}
|
|
2371
|
+
function classifyJobs(events) {
|
|
2372
|
+
const grouped = groupByLatest(events);
|
|
2373
|
+
const jobs = [];
|
|
2374
|
+
for (const [id, evs] of grouped) {
|
|
2375
|
+
jobs.push(jobFromEvents(id, evs));
|
|
2376
|
+
}
|
|
2377
|
+
jobs.sort((a, b) => b.latestTs.localeCompare(a.latestTs));
|
|
2378
|
+
return jobs;
|
|
2379
|
+
}
|
|
2380
|
+
var _cache = null;
|
|
2381
|
+
function readLedger(env = process.env) {
|
|
2382
|
+
const path = resolveLedgerPath(env);
|
|
2383
|
+
if (!existsSync2(path)) {
|
|
2384
|
+
return { jobs: [], error: `Ledger not found: ${path}` };
|
|
2385
|
+
}
|
|
2386
|
+
try {
|
|
2387
|
+
const mtime = statSync(path).mtimeMs;
|
|
2388
|
+
if (_cache && _cache.path === path && _cache.mtime === mtime) {
|
|
2389
|
+
return { jobs: _cache.jobs };
|
|
2390
|
+
}
|
|
2391
|
+
const text = readFileSync3(path, "utf8");
|
|
2392
|
+
const jobs = classifyJobs(parseJobsJsonl(text));
|
|
2393
|
+
_cache = { path, mtime, jobs };
|
|
2394
|
+
return { jobs };
|
|
2395
|
+
} catch (err) {
|
|
2396
|
+
return { jobs: [], error: String(err) };
|
|
2397
|
+
}
|
|
2398
|
+
}
|
|
2399
|
+
|
|
2275
2400
|
// src/app-api.ts
|
|
2276
2401
|
function json(res, status, body) {
|
|
2277
2402
|
res.statusCode = status;
|
|
@@ -2416,6 +2541,26 @@ function handleAppApi(req, res, app, opts = {}) {
|
|
|
2416
2541
|
});
|
|
2417
2542
|
return true;
|
|
2418
2543
|
}
|
|
2544
|
+
if (url.pathname === "/api/factory/jobs" && method === "GET") {
|
|
2545
|
+
reply(req, res, async () => {
|
|
2546
|
+
const result = readLedger();
|
|
2547
|
+
json(res, result.error && result.jobs.length === 0 ? 503 : 200, result);
|
|
2548
|
+
});
|
|
2549
|
+
return true;
|
|
2550
|
+
}
|
|
2551
|
+
if (/^\/api\/factory\/jobs\/[^/]+$/.test(url.pathname) && method === "GET") {
|
|
2552
|
+
reply(req, res, async () => {
|
|
2553
|
+
const id = url.pathname.slice("/api/factory/jobs/".length);
|
|
2554
|
+
const result = readLedger();
|
|
2555
|
+
const job = result.jobs.find((j) => j.id === id);
|
|
2556
|
+
if (!job) {
|
|
2557
|
+
json(res, 404, { error: `Job not found: ${id}`, ledgerError: result.error });
|
|
2558
|
+
return;
|
|
2559
|
+
}
|
|
2560
|
+
json(res, 200, { job });
|
|
2561
|
+
});
|
|
2562
|
+
return true;
|
|
2563
|
+
}
|
|
2419
2564
|
return false;
|
|
2420
2565
|
}
|
|
2421
2566
|
|
|
@@ -2505,10 +2650,10 @@ function formatCard(card) {
|
|
|
2505
2650
|
}
|
|
2506
2651
|
|
|
2507
2652
|
// src/server/agent/config.ts
|
|
2508
|
-
import { existsSync as
|
|
2509
|
-
import { homedir as
|
|
2510
|
-
import { delimiter as delimiter2, dirname as dirname2, join as
|
|
2511
|
-
var DEFAULT_CONFIG_PATH =
|
|
2653
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "node:fs";
|
|
2654
|
+
import { homedir as homedir4 } from "node:os";
|
|
2655
|
+
import { delimiter as delimiter2, dirname as dirname2, join as join5 } from "node:path";
|
|
2656
|
+
var DEFAULT_CONFIG_PATH = join5(homedir4(), ".config", "pipe-kan", "agent.json");
|
|
2512
2657
|
var FALLBACK_MODELS = {
|
|
2513
2658
|
cursor: [
|
|
2514
2659
|
{ id: "composer-2", name: "Composer 2" },
|
|
@@ -2550,21 +2695,21 @@ function commandOnPath(command, env = process.env) {
|
|
|
2550
2695
|
if (!command)
|
|
2551
2696
|
return false;
|
|
2552
2697
|
if (command.includes("/") || command.includes("\\"))
|
|
2553
|
-
return
|
|
2698
|
+
return existsSync3(command);
|
|
2554
2699
|
const pathVar = env.PATH ?? "";
|
|
2555
2700
|
for (const dir of pathVar.split(delimiter2)) {
|
|
2556
2701
|
if (!dir)
|
|
2557
2702
|
continue;
|
|
2558
|
-
if (
|
|
2703
|
+
if (existsSync3(join5(dir, command)))
|
|
2559
2704
|
return true;
|
|
2560
2705
|
}
|
|
2561
2706
|
return false;
|
|
2562
2707
|
}
|
|
2563
2708
|
function loadAgentConfig(path = agentConfigPath()) {
|
|
2564
|
-
if (!
|
|
2709
|
+
if (!existsSync3(path))
|
|
2565
2710
|
return structuredClone(DEFAULT_CONFIG);
|
|
2566
2711
|
try {
|
|
2567
|
-
const raw = JSON.parse(
|
|
2712
|
+
const raw = JSON.parse(readFileSync4(path, "utf8"));
|
|
2568
2713
|
return mergeConfig(raw);
|
|
2569
2714
|
} catch {
|
|
2570
2715
|
return structuredClone(DEFAULT_CONFIG);
|
|
@@ -2600,7 +2745,7 @@ function mergeConfig(raw) {
|
|
|
2600
2745
|
}
|
|
2601
2746
|
function ensureAgentConfigDir(path = agentConfigPath()) {
|
|
2602
2747
|
const dir = dirname2(path);
|
|
2603
|
-
if (!
|
|
2748
|
+
if (!existsSync3(dir))
|
|
2604
2749
|
mkdirSync2(dir, { recursive: true });
|
|
2605
2750
|
}
|
|
2606
2751
|
|
|
@@ -2608,7 +2753,7 @@ function ensureAgentConfigDir(path = agentConfigPath()) {
|
|
|
2608
2753
|
import { spawn as spawn2 } from "node:child_process";
|
|
2609
2754
|
import { mkdtempSync, rmSync } from "node:fs";
|
|
2610
2755
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
2611
|
-
import { join as
|
|
2756
|
+
import { join as join6 } from "node:path";
|
|
2612
2757
|
import { Readable } from "node:stream";
|
|
2613
2758
|
|
|
2614
2759
|
// node_modules/@agentclientprotocol/sdk/dist/schema/index.js
|
|
@@ -12334,7 +12479,7 @@ class AcpSession {
|
|
|
12334
12479
|
executeTool = null;
|
|
12335
12480
|
stderr = "";
|
|
12336
12481
|
selectedModelId = null;
|
|
12337
|
-
workspace = mkdtempSync(
|
|
12482
|
+
workspace = mkdtempSync(join6(tmpdir2(), "pipe-kan-agent-"));
|
|
12338
12483
|
constructor(config) {
|
|
12339
12484
|
this.config = config;
|
|
12340
12485
|
this.id = crypto.randomUUID();
|
|
@@ -12646,17 +12791,17 @@ function permissionOptionForDecision(options, decision) {
|
|
|
12646
12791
|
}
|
|
12647
12792
|
|
|
12648
12793
|
// src/server/agent/skills.ts
|
|
12649
|
-
import { existsSync as
|
|
12650
|
-
import { homedir as
|
|
12651
|
-
import { join as
|
|
12794
|
+
import { existsSync as existsSync4, readFileSync as readFileSync5, readdirSync } from "node:fs";
|
|
12795
|
+
import { homedir as homedir5 } from "node:os";
|
|
12796
|
+
import { join as join7 } from "node:path";
|
|
12652
12797
|
import { fileURLToPath } from "node:url";
|
|
12653
12798
|
function createSkillRegistry(bundledDir) {
|
|
12654
|
-
const dir = bundledDir ??
|
|
12655
|
-
const userDir =
|
|
12799
|
+
const dir = bundledDir ?? join7(fileURLToPath(new URL(".", import.meta.url)), "..", "..", "..", ".agents", "skills");
|
|
12800
|
+
const userDir = join7(homedir5(), ".pi", "agent", "skills");
|
|
12656
12801
|
return {
|
|
12657
12802
|
list() {
|
|
12658
12803
|
const bundled = listSkills(dir);
|
|
12659
|
-
const user =
|
|
12804
|
+
const user = existsSync4(userDir) ? listSkills(userDir) : [];
|
|
12660
12805
|
const map = new Map;
|
|
12661
12806
|
for (const skill of bundled)
|
|
12662
12807
|
map.set(skill.id, skill);
|
|
@@ -12666,10 +12811,10 @@ function createSkillRegistry(bundledDir) {
|
|
|
12666
12811
|
},
|
|
12667
12812
|
load(id) {
|
|
12668
12813
|
const userPath = skillPath(userDir, id);
|
|
12669
|
-
if (
|
|
12814
|
+
if (existsSync4(userPath))
|
|
12670
12815
|
return readSkill(userPath, id);
|
|
12671
12816
|
const bundledPath = skillPath(dir, id);
|
|
12672
|
-
if (
|
|
12817
|
+
if (existsSync4(bundledPath))
|
|
12673
12818
|
return readSkill(bundledPath, id);
|
|
12674
12819
|
return;
|
|
12675
12820
|
}
|
|
@@ -12686,16 +12831,16 @@ function skillContextBlock(skill) {
|
|
|
12686
12831
|
};
|
|
12687
12832
|
}
|
|
12688
12833
|
function listSkills(dir) {
|
|
12689
|
-
if (!
|
|
12834
|
+
if (!existsSync4(dir))
|
|
12690
12835
|
return [];
|
|
12691
12836
|
return readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => readSkill(skillPath(dir, entry.name), entry.name)).filter((skill) => skill !== undefined);
|
|
12692
12837
|
}
|
|
12693
12838
|
function skillPath(dir, id) {
|
|
12694
|
-
return
|
|
12839
|
+
return join7(dir, id, "SKILL.md");
|
|
12695
12840
|
}
|
|
12696
12841
|
function readSkill(path, id) {
|
|
12697
12842
|
try {
|
|
12698
|
-
const text =
|
|
12843
|
+
const text = readFileSync5(path, "utf8");
|
|
12699
12844
|
const front = parseFrontMatter(text);
|
|
12700
12845
|
return {
|
|
12701
12846
|
id,
|
|
@@ -12722,8 +12867,8 @@ function parseFrontMatter(text) {
|
|
|
12722
12867
|
}
|
|
12723
12868
|
|
|
12724
12869
|
// src/server/agent/tools.ts
|
|
12725
|
-
import { readFileSync as
|
|
12726
|
-
import { join as
|
|
12870
|
+
import { mkdirSync as mkdirSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync3 } from "node:fs";
|
|
12871
|
+
import { join as join8 } from "node:path";
|
|
12727
12872
|
var TOOLS = [
|
|
12728
12873
|
{
|
|
12729
12874
|
name: "board_state",
|
|
@@ -12799,6 +12944,35 @@ var TOOLS = [
|
|
|
12799
12944
|
labels: { type: "string", description: "Comma-separated replacement labels" }
|
|
12800
12945
|
},
|
|
12801
12946
|
mutates: true
|
|
12947
|
+
},
|
|
12948
|
+
{
|
|
12949
|
+
name: "factory_job_status",
|
|
12950
|
+
description: "Read the latest status and factory stage tag for one or more software-factory jobs from the local ledger. Pass a job id (gbj-YYYYMMDD-NNN) for a single job, or omit id to get the 20 most-recent active jobs.",
|
|
12951
|
+
parameters: {
|
|
12952
|
+
id: { type: "string", description: "Optional job id, e.g. gbj-20260926-011" }
|
|
12953
|
+
},
|
|
12954
|
+
mutates: false
|
|
12955
|
+
},
|
|
12956
|
+
{
|
|
12957
|
+
name: "factory_start_job",
|
|
12958
|
+
description: "Request the Grokbot Coordinator to start a new factory job. Writes a start-request to docs/factory/outbox/ for the coordinator to pick up. Requires user approval. Does NOT append to jobs.jsonl directly.",
|
|
12959
|
+
parameters: {
|
|
12960
|
+
summary: { type: "string", description: "One-line job summary" },
|
|
12961
|
+
repo: { type: "string", description: "Target repo path, e.g. ~/code/pipe-kan" },
|
|
12962
|
+
session: { type: "string", description: "Herdr session name, e.g. jira-kan" },
|
|
12963
|
+
notes: { type: "string", description: "Optional extra context for the coordinator" }
|
|
12964
|
+
},
|
|
12965
|
+
mutates: true
|
|
12966
|
+
},
|
|
12967
|
+
{
|
|
12968
|
+
name: "factory_move_job",
|
|
12969
|
+
description: "Request the Grokbot Coordinator to move a factory job to a new status or stage. Writes a move-request to docs/factory/outbox/ for the coordinator to pick up. Requires user approval. Does NOT append to jobs.jsonl directly.",
|
|
12970
|
+
parameters: {
|
|
12971
|
+
id: { type: "string", description: "Job id to move, e.g. gbj-20260926-011" },
|
|
12972
|
+
status: { type: "string", description: "Target status: queued|routed|in_progress|blocked|done|cancelled" },
|
|
12973
|
+
detail: { type: "string", description: "Optional detail message for the ledger event" }
|
|
12974
|
+
},
|
|
12975
|
+
mutates: true
|
|
12802
12976
|
}
|
|
12803
12977
|
];
|
|
12804
12978
|
var skills;
|
|
@@ -12843,7 +13017,7 @@ var EXECUTORS = {
|
|
|
12843
13017
|
return { ok: false, error: "Path traversal not allowed" };
|
|
12844
13018
|
const repoRoot = process.cwd();
|
|
12845
13019
|
try {
|
|
12846
|
-
const text =
|
|
13020
|
+
const text = readFileSync6(join8(repoRoot, relPath), "utf8");
|
|
12847
13021
|
return { ok: true, value: { path: relPath, text } };
|
|
12848
13022
|
} catch (err) {
|
|
12849
13023
|
return { ok: false, error: String(err) };
|
|
@@ -12914,6 +13088,93 @@ var EXECUTORS = {
|
|
|
12914
13088
|
if (result.error)
|
|
12915
13089
|
return { ok: false, error: result.error };
|
|
12916
13090
|
return { ok: true, value: { key, __ui_action: "refresh_board" } };
|
|
13091
|
+
},
|
|
13092
|
+
factory_job_status(args) {
|
|
13093
|
+
const id = typeof args.id === "string" ? args.id.trim() : "";
|
|
13094
|
+
const ledger = readLedger();
|
|
13095
|
+
if (id) {
|
|
13096
|
+
const job = ledger.jobs.find((j) => j.id === id);
|
|
13097
|
+
if (!job) {
|
|
13098
|
+
return { ok: false, error: `Job not found: ${id}${ledger.error ? ` (${ledger.error})` : ""}` };
|
|
13099
|
+
}
|
|
13100
|
+
return { ok: true, value: { job, ledgerError: ledger.error } };
|
|
13101
|
+
}
|
|
13102
|
+
const active = ledger.jobs.filter((j) => j.stageTag !== null).slice(0, 20);
|
|
13103
|
+
return {
|
|
13104
|
+
ok: true,
|
|
13105
|
+
value: { jobs: active, total: ledger.jobs.length, ledgerError: ledger.error }
|
|
13106
|
+
};
|
|
13107
|
+
},
|
|
13108
|
+
factory_start_job(args) {
|
|
13109
|
+
const summary = String(args.summary ?? "").trim();
|
|
13110
|
+
if (!summary)
|
|
13111
|
+
return { ok: false, error: "Missing summary" };
|
|
13112
|
+
const repo = String(args.repo ?? "").trim();
|
|
13113
|
+
const session = String(args.session ?? "").trim();
|
|
13114
|
+
const notes = typeof args.notes === "string" ? args.notes.trim() : undefined;
|
|
13115
|
+
const ts = new Date().toISOString();
|
|
13116
|
+
const requestId = crypto.randomUUID();
|
|
13117
|
+
const outboxDir = join8(process.cwd(), "docs/factory/outbox");
|
|
13118
|
+
mkdirSync3(outboxDir, { recursive: true });
|
|
13119
|
+
const filename = `${ts.replace(/[:.]/g, "-")}-${requestId}-start.json`;
|
|
13120
|
+
const payload = {
|
|
13121
|
+
action: "start_job",
|
|
13122
|
+
requested_by: "pipe-kan-acp",
|
|
13123
|
+
ts,
|
|
13124
|
+
requestId,
|
|
13125
|
+
summary,
|
|
13126
|
+
...repo ? { repo } : {},
|
|
13127
|
+
...session ? { session } : {},
|
|
13128
|
+
...notes ? { notes } : {}
|
|
13129
|
+
};
|
|
13130
|
+
writeFileSync3(join8(outboxDir, filename), JSON.stringify(payload, null, 2) + `
|
|
13131
|
+
`, "utf8");
|
|
13132
|
+
return {
|
|
13133
|
+
ok: true,
|
|
13134
|
+
value: {
|
|
13135
|
+
message: `Start request written to docs/factory/outbox/${filename}. The Grokbot Coordinator will pick this up and append the real ledger event.`,
|
|
13136
|
+
outboxFile: filename
|
|
13137
|
+
}
|
|
13138
|
+
};
|
|
13139
|
+
},
|
|
13140
|
+
factory_move_job(args) {
|
|
13141
|
+
const id = String(args.id ?? "").trim();
|
|
13142
|
+
const status = String(args.status ?? "").trim();
|
|
13143
|
+
if (!id)
|
|
13144
|
+
return { ok: false, error: "Missing job id" };
|
|
13145
|
+
if (!status)
|
|
13146
|
+
return { ok: false, error: "Missing target status" };
|
|
13147
|
+
const validStatuses = ["queued", "routed", "in_progress", "blocked", "done", "cancelled"];
|
|
13148
|
+
if (!validStatuses.includes(status)) {
|
|
13149
|
+
return {
|
|
13150
|
+
ok: false,
|
|
13151
|
+
error: `Invalid status '${status}'. Must be one of: ${validStatuses.join(", ")}`
|
|
13152
|
+
};
|
|
13153
|
+
}
|
|
13154
|
+
const detail = typeof args.detail === "string" ? args.detail.trim() : undefined;
|
|
13155
|
+
const ts = new Date().toISOString();
|
|
13156
|
+
const requestId = crypto.randomUUID();
|
|
13157
|
+
const outboxDir = join8(process.cwd(), "docs/factory/outbox");
|
|
13158
|
+
mkdirSync3(outboxDir, { recursive: true });
|
|
13159
|
+
const filename = `${ts.replace(/[:.]/g, "-")}-${requestId}-move.json`;
|
|
13160
|
+
const payload = {
|
|
13161
|
+
action: "move_job",
|
|
13162
|
+
requested_by: "pipe-kan-acp",
|
|
13163
|
+
ts,
|
|
13164
|
+
requestId,
|
|
13165
|
+
id,
|
|
13166
|
+
status,
|
|
13167
|
+
...detail ? { detail } : {}
|
|
13168
|
+
};
|
|
13169
|
+
writeFileSync3(join8(outboxDir, filename), JSON.stringify(payload, null, 2) + `
|
|
13170
|
+
`, "utf8");
|
|
13171
|
+
return {
|
|
13172
|
+
ok: true,
|
|
13173
|
+
value: {
|
|
13174
|
+
message: `Move request for ${id} → ${status} written to docs/factory/outbox/${filename}. The Grokbot Coordinator will pick this up and append the real ledger event.`,
|
|
13175
|
+
outboxFile: filename
|
|
13176
|
+
}
|
|
13177
|
+
};
|
|
12917
13178
|
}
|
|
12918
13179
|
};
|
|
12919
13180
|
var SYSTEM_TEXT = `Your working context is the attached Jira issues from the pipe. Answer from that payload. Do not search GitHub, git history, or local source files unless the user explicitly asks about the pipe-kan app itself.
|
|
@@ -13389,12 +13650,12 @@ function handleRequest(req, res, ctx) {
|
|
|
13389
13650
|
}
|
|
13390
13651
|
|
|
13391
13652
|
// src/jira-config.ts
|
|
13392
|
-
import { mkdirSync as
|
|
13393
|
-
import { join as
|
|
13653
|
+
import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "node:fs";
|
|
13654
|
+
import { join as join9 } from "node:path";
|
|
13394
13655
|
function writeJiraConfig(dir, server) {
|
|
13395
|
-
|
|
13396
|
-
const path =
|
|
13397
|
-
|
|
13656
|
+
mkdirSync4(dir, { recursive: true });
|
|
13657
|
+
const path = join9(dir, "jira.config.yml");
|
|
13658
|
+
writeFileSync4(path, [
|
|
13398
13659
|
"installation: Cloud",
|
|
13399
13660
|
`server: ${server}`,
|
|
13400
13661
|
`login: ${ME.emailAddress}`,
|
|
@@ -13448,8 +13709,8 @@ function stdinStat() {
|
|
|
13448
13709
|
}
|
|
13449
13710
|
|
|
13450
13711
|
// src/ui.ts
|
|
13451
|
-
import { createReadStream, existsSync as
|
|
13452
|
-
import { dirname as dirname3, extname, join as
|
|
13712
|
+
import { createReadStream, existsSync as existsSync5, statSync as statSync2 } from "node:fs";
|
|
13713
|
+
import { dirname as dirname3, extname, join as join10, resolve as resolve2, sep } from "node:path";
|
|
13453
13714
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
13454
13715
|
var types = {
|
|
13455
13716
|
".css": "text/css; charset=utf-8",
|
|
@@ -13466,7 +13727,7 @@ function packageRoot(from = import.meta.url) {
|
|
|
13466
13727
|
return resolve2(dirname3(fileURLToPath2(from)), "..");
|
|
13467
13728
|
}
|
|
13468
13729
|
function uiDir(root) {
|
|
13469
|
-
return
|
|
13730
|
+
return join10(root, "dist", "ui");
|
|
13470
13731
|
}
|
|
13471
13732
|
function inside(root, file) {
|
|
13472
13733
|
const base = resolve2(root);
|
|
@@ -13475,12 +13736,12 @@ function inside(root, file) {
|
|
|
13475
13736
|
}
|
|
13476
13737
|
function sendUi(root, req, res) {
|
|
13477
13738
|
const ui = uiDir(root);
|
|
13478
|
-
const index =
|
|
13479
|
-
if (!
|
|
13739
|
+
const index = join10(ui, "index.html");
|
|
13740
|
+
if (!existsSync5(index))
|
|
13480
13741
|
return false;
|
|
13481
13742
|
const path = new URL(req.url ?? "/", "http://127.0.0.1").pathname;
|
|
13482
13743
|
const wanted = resolve2(ui, `.${decodeURIComponent(path)}`);
|
|
13483
|
-
const file = inside(ui, wanted) &&
|
|
13744
|
+
const file = inside(ui, wanted) && existsSync5(wanted) && statSync2(wanted).isFile() ? wanted : index;
|
|
13484
13745
|
res.setHeader("content-type", types[extname(file)] ?? "application/octet-stream");
|
|
13485
13746
|
createReadStream(file).pipe(res);
|
|
13486
13747
|
return true;
|
|
@@ -13496,7 +13757,7 @@ function announce(line) {
|
|
|
13496
13757
|
}
|
|
13497
13758
|
async function runServer(opts) {
|
|
13498
13759
|
const piped = await readPipe();
|
|
13499
|
-
const raw = piped ?? JSON.parse(
|
|
13760
|
+
const raw = piped ?? JSON.parse(readFileSync7(join11(opts.root, "fixtures/issues.json"), "utf8"));
|
|
13500
13761
|
const flags = argvToFlags(process.argv);
|
|
13501
13762
|
const { app, store, kind } = await createBoardApp({
|
|
13502
13763
|
raw,
|
|
@@ -13533,7 +13794,7 @@ async function runServer(opts) {
|
|
|
13533
13794
|
announce(`cli plane ${planeHost()} ${planeWorkspace(flags)}`);
|
|
13534
13795
|
announce(`Plane host default ${DEFAULT_PLANE_HOST}`);
|
|
13535
13796
|
} else {
|
|
13536
|
-
const fakeConfig = writeJiraConfig(
|
|
13797
|
+
const fakeConfig = writeJiraConfig(join11(tmpdir3(), "pipe-kan"), origin);
|
|
13537
13798
|
announce(`cli ${kind === "jira" ? resolveJiraBin() : "store"}`);
|
|
13538
13799
|
announce(`Fake Jira ${origin}/rest/api/2/search`);
|
|
13539
13800
|
announce(`Fake Jira config ${fakeConfig}`);
|