mewkit 1.7.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +76 -0
- package/dist/commands/memory.js +3 -3
- package/dist/commands/memory.js.map +1 -1
- package/dist/commands/orchviz.d.ts +12 -0
- package/dist/commands/orchviz.d.ts.map +1 -0
- package/dist/commands/orchviz.js +160 -0
- package/dist/commands/orchviz.js.map +1 -0
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +7 -5
- package/dist/commands/setup.js.map +1 -1
- package/dist/core/skills-dependencies.js +2 -2
- package/dist/core/skills-dependencies.js.map +1 -1
- package/dist/index.js +50 -1
- package/dist/index.js.map +1 -1
- package/dist/migrate/__tests__/skill-id-utils.test.d.ts +2 -0
- package/dist/migrate/__tests__/skill-id-utils.test.d.ts.map +1 -0
- package/dist/migrate/__tests__/skill-id-utils.test.js +78 -0
- package/dist/migrate/__tests__/skill-id-utils.test.js.map +1 -0
- package/dist/migrate/discovery/skill-id-utils.d.ts +18 -0
- package/dist/migrate/discovery/skill-id-utils.d.ts.map +1 -0
- package/dist/migrate/discovery/skill-id-utils.js +45 -0
- package/dist/migrate/discovery/skill-id-utils.js.map +1 -0
- package/dist/migrate/discovery/skills-discovery.d.ts.map +1 -1
- package/dist/migrate/discovery/skills-discovery.js +5 -1
- package/dist/migrate/discovery/skills-discovery.js.map +1 -1
- package/dist/migrate/reconcile/portable-registry.d.ts +12 -12
- package/dist/migrate/types.d.ts +8 -6
- package/dist/migrate/types.d.ts.map +1 -1
- package/dist/migrate/types.js.map +1 -1
- package/dist/orchviz/constants.d.ts +53 -0
- package/dist/orchviz/constants.d.ts.map +1 -0
- package/dist/orchviz/constants.js +68 -0
- package/dist/orchviz/constants.js.map +1 -0
- package/dist/orchviz/fs-utils.d.ts +24 -0
- package/dist/orchviz/fs-utils.d.ts.map +1 -0
- package/dist/orchviz/fs-utils.js +49 -0
- package/dist/orchviz/fs-utils.js.map +1 -0
- package/dist/orchviz/index.d.ts +29 -0
- package/dist/orchviz/index.d.ts.map +1 -0
- package/dist/orchviz/index.js +28 -0
- package/dist/orchviz/index.js.map +1 -0
- package/dist/orchviz/log-persister.d.ts +22 -0
- package/dist/orchviz/log-persister.d.ts.map +1 -0
- package/dist/orchviz/log-persister.js +96 -0
- package/dist/orchviz/log-persister.js.map +1 -0
- package/dist/orchviz/logger.d.ts +13 -0
- package/dist/orchviz/logger.d.ts.map +1 -0
- package/dist/orchviz/logger.js +47 -0
- package/dist/orchviz/logger.js.map +1 -0
- package/dist/orchviz/open-url.d.ts +11 -0
- package/dist/orchviz/open-url.d.ts.map +1 -0
- package/dist/orchviz/open-url.js +28 -0
- package/dist/orchviz/open-url.js.map +1 -0
- package/dist/orchviz/overlay/collector.d.ts +29 -0
- package/dist/orchviz/overlay/collector.d.ts.map +1 -0
- package/dist/orchviz/overlay/collector.js +38 -0
- package/dist/orchviz/overlay/collector.js.map +1 -0
- package/dist/orchviz/overlay/gate-readers.d.ts +18 -0
- package/dist/orchviz/overlay/gate-readers.d.ts.map +1 -0
- package/dist/orchviz/overlay/gate-readers.js +111 -0
- package/dist/orchviz/overlay/gate-readers.js.map +1 -0
- package/dist/orchviz/overlay/session-state-readers.d.ts +18 -0
- package/dist/orchviz/overlay/session-state-readers.d.ts.map +1 -0
- package/dist/orchviz/overlay/session-state-readers.js +78 -0
- package/dist/orchviz/overlay/session-state-readers.js.map +1 -0
- package/dist/orchviz/parser/handle-progress.d.ts +9 -0
- package/dist/orchviz/parser/handle-progress.d.ts.map +1 -0
- package/dist/orchviz/parser/handle-progress.js +38 -0
- package/dist/orchviz/parser/handle-progress.js.map +1 -0
- package/dist/orchviz/parser/handle-text.d.ts +10 -0
- package/dist/orchviz/parser/handle-text.d.ts.map +1 -0
- package/dist/orchviz/parser/handle-text.js +38 -0
- package/dist/orchviz/parser/handle-text.js.map +1 -0
- package/dist/orchviz/parser/handle-thinking.d.ts +10 -0
- package/dist/orchviz/parser/handle-thinking.d.ts.map +1 -0
- package/dist/orchviz/parser/handle-thinking.js +35 -0
- package/dist/orchviz/parser/handle-thinking.js.map +1 -0
- package/dist/orchviz/parser/handle-tool-result.d.ts +10 -0
- package/dist/orchviz/parser/handle-tool-result.d.ts.map +1 -0
- package/dist/orchviz/parser/handle-tool-result.js +62 -0
- package/dist/orchviz/parser/handle-tool-result.js.map +1 -0
- package/dist/orchviz/parser/handle-tool-use.d.ts +11 -0
- package/dist/orchviz/parser/handle-tool-use.d.ts.map +1 -0
- package/dist/orchviz/parser/handle-tool-use.js +38 -0
- package/dist/orchviz/parser/handle-tool-use.js.map +1 -0
- package/dist/orchviz/parser/index.d.ts +38 -0
- package/dist/orchviz/parser/index.d.ts.map +1 -0
- package/dist/orchviz/parser/index.js +139 -0
- package/dist/orchviz/parser/index.js.map +1 -0
- package/dist/orchviz/parser/label-helpers.d.ts +9 -0
- package/dist/orchviz/parser/label-helpers.d.ts.map +1 -0
- package/dist/orchviz/parser/label-helpers.js +44 -0
- package/dist/orchviz/parser/label-helpers.js.map +1 -0
- package/dist/orchviz/parser/strip-ansi.d.ts +6 -0
- package/dist/orchviz/parser/strip-ansi.d.ts.map +1 -0
- package/dist/orchviz/parser/strip-ansi.js +14 -0
- package/dist/orchviz/parser/strip-ansi.js.map +1 -0
- package/dist/orchviz/parser/utils.d.ts +20 -0
- package/dist/orchviz/parser/utils.d.ts.map +1 -0
- package/dist/orchviz/parser/utils.js +56 -0
- package/dist/orchviz/parser/utils.js.map +1 -0
- package/dist/orchviz/permission-detection.d.ts +18 -0
- package/dist/orchviz/permission-detection.d.ts.map +1 -0
- package/dist/orchviz/permission-detection.js +50 -0
- package/dist/orchviz/permission-detection.js.map +1 -0
- package/dist/orchviz/plan/__tests__/apply-todo-toggle.test.d.ts +15 -0
- package/dist/orchviz/plan/__tests__/apply-todo-toggle.test.d.ts.map +1 -0
- package/dist/orchviz/plan/__tests__/apply-todo-toggle.test.js +165 -0
- package/dist/orchviz/plan/__tests__/apply-todo-toggle.test.js.map +1 -0
- package/dist/orchviz/plan/__tests__/atomic-write.test.d.ts +11 -0
- package/dist/orchviz/plan/__tests__/atomic-write.test.d.ts.map +1 -0
- package/dist/orchviz/plan/__tests__/atomic-write.test.js +89 -0
- package/dist/orchviz/plan/__tests__/atomic-write.test.js.map +1 -0
- package/dist/orchviz/plan/__tests__/end-to-end.test.d.ts +10 -0
- package/dist/orchviz/plan/__tests__/end-to-end.test.d.ts.map +1 -0
- package/dist/orchviz/plan/__tests__/end-to-end.test.js +132 -0
- package/dist/orchviz/plan/__tests__/end-to-end.test.js.map +1 -0
- package/dist/orchviz/plan/__tests__/etag.test.d.ts +11 -0
- package/dist/orchviz/plan/__tests__/etag.test.d.ts.map +1 -0
- package/dist/orchviz/plan/__tests__/etag.test.js +92 -0
- package/dist/orchviz/plan/__tests__/etag.test.js.map +1 -0
- package/dist/orchviz/plan/__tests__/list-plans.test.d.ts +14 -0
- package/dist/orchviz/plan/__tests__/list-plans.test.d.ts.map +1 -0
- package/dist/orchviz/plan/__tests__/list-plans.test.js +154 -0
- package/dist/orchviz/plan/__tests__/list-plans.test.js.map +1 -0
- package/dist/orchviz/plan/apply-todo-toggle.d.ts +29 -0
- package/dist/orchviz/plan/apply-todo-toggle.d.ts.map +1 -0
- package/dist/orchviz/plan/apply-todo-toggle.js +129 -0
- package/dist/orchviz/plan/apply-todo-toggle.js.map +1 -0
- package/dist/orchviz/plan/atomic-write.d.ts +25 -0
- package/dist/orchviz/plan/atomic-write.d.ts.map +1 -0
- package/dist/orchviz/plan/atomic-write.js +85 -0
- package/dist/orchviz/plan/atomic-write.js.map +1 -0
- package/dist/orchviz/plan/collector.d.ts +49 -0
- package/dist/orchviz/plan/collector.d.ts.map +1 -0
- package/dist/orchviz/plan/collector.js +173 -0
- package/dist/orchviz/plan/collector.js.map +1 -0
- package/dist/orchviz/plan/etag.d.ts +24 -0
- package/dist/orchviz/plan/etag.d.ts.map +1 -0
- package/dist/orchviz/plan/etag.js +58 -0
- package/dist/orchviz/plan/etag.js.map +1 -0
- package/dist/orchviz/plan/find-active-plan.d.ts +9 -0
- package/dist/orchviz/plan/find-active-plan.d.ts.map +1 -0
- package/dist/orchviz/plan/find-active-plan.js +90 -0
- package/dist/orchviz/plan/find-active-plan.js.map +1 -0
- package/dist/orchviz/plan/index.d.ts +23 -0
- package/dist/orchviz/plan/index.d.ts.map +1 -0
- package/dist/orchviz/plan/index.js +91 -0
- package/dist/orchviz/plan/index.js.map +1 -0
- package/dist/orchviz/plan/list-plans.d.ts +19 -0
- package/dist/orchviz/plan/list-plans.d.ts.map +1 -0
- package/dist/orchviz/plan/list-plans.js +148 -0
- package/dist/orchviz/plan/list-plans.js.map +1 -0
- package/dist/orchviz/plan/parse-phase-file.d.ts +13 -0
- package/dist/orchviz/plan/parse-phase-file.d.ts.map +1 -0
- package/dist/orchviz/plan/parse-phase-file.js +136 -0
- package/dist/orchviz/plan/parse-phase-file.js.map +1 -0
- package/dist/orchviz/plan/parse-plan-file.d.ts +19 -0
- package/dist/orchviz/plan/parse-plan-file.d.ts.map +1 -0
- package/dist/orchviz/plan/parse-plan-file.js +62 -0
- package/dist/orchviz/plan/parse-plan-file.js.map +1 -0
- package/dist/orchviz/plan/plan-constants.d.ts +17 -0
- package/dist/orchviz/plan/plan-constants.d.ts.map +1 -0
- package/dist/orchviz/plan/plan-constants.js +19 -0
- package/dist/orchviz/plan/plan-constants.js.map +1 -0
- package/dist/orchviz/plan/types.d.ts +46 -0
- package/dist/orchviz/plan/types.d.ts.map +1 -0
- package/dist/orchviz/plan/types.js +6 -0
- package/dist/orchviz/plan/types.js.map +1 -0
- package/dist/orchviz/protocol.d.ts +116 -0
- package/dist/orchviz/protocol.d.ts.map +1 -0
- package/dist/orchviz/protocol.js +23 -0
- package/dist/orchviz/protocol.js.map +1 -0
- package/dist/orchviz/redact.d.ts +10 -0
- package/dist/orchviz/redact.d.ts.map +1 -0
- package/dist/orchviz/redact.js +38 -0
- package/dist/orchviz/redact.js.map +1 -0
- package/dist/orchviz/sanitize.d.ts +11 -0
- package/dist/orchviz/sanitize.d.ts.map +1 -0
- package/dist/orchviz/sanitize.js +38 -0
- package/dist/orchviz/sanitize.js.map +1 -0
- package/dist/orchviz/server/__tests__/security-smoke.test.d.ts +13 -0
- package/dist/orchviz/server/__tests__/security-smoke.test.d.ts.map +1 -0
- package/dist/orchviz/server/__tests__/security-smoke.test.js +207 -0
- package/dist/orchviz/server/__tests__/security-smoke.test.js.map +1 -0
- package/dist/orchviz/server/__tests__/write-handlers.test.d.ts +24 -0
- package/dist/orchviz/server/__tests__/write-handlers.test.d.ts.map +1 -0
- package/dist/orchviz/server/__tests__/write-handlers.test.js +347 -0
- package/dist/orchviz/server/__tests__/write-handlers.test.js.map +1 -0
- package/dist/orchviz/server/api-handlers.d.ts +55 -0
- package/dist/orchviz/server/api-handlers.d.ts.map +1 -0
- package/dist/orchviz/server/api-handlers.js +112 -0
- package/dist/orchviz/server/api-handlers.js.map +1 -0
- package/dist/orchviz/server/index.d.ts +36 -0
- package/dist/orchviz/server/index.d.ts.map +1 -0
- package/dist/orchviz/server/index.js +147 -0
- package/dist/orchviz/server/index.js.map +1 -0
- package/dist/orchviz/server/sse-handler.d.ts +25 -0
- package/dist/orchviz/server/sse-handler.d.ts.map +1 -0
- package/dist/orchviz/server/sse-handler.js +128 -0
- package/dist/orchviz/server/sse-handler.js.map +1 -0
- package/dist/orchviz/server/static-handler.d.ts +9 -0
- package/dist/orchviz/server/static-handler.d.ts.map +1 -0
- package/dist/orchviz/server/static-handler.js +76 -0
- package/dist/orchviz/server/static-handler.js.map +1 -0
- package/dist/orchviz/server/write-handlers.d.ts +20 -0
- package/dist/orchviz/server/write-handlers.d.ts.map +1 -0
- package/dist/orchviz/server/write-handlers.js +167 -0
- package/dist/orchviz/server/write-handlers.js.map +1 -0
- package/dist/orchviz/server/write-utils.d.ts +59 -0
- package/dist/orchviz/server/write-utils.d.ts.map +1 -0
- package/dist/orchviz/server/write-utils.js +161 -0
- package/dist/orchviz/server/write-utils.js.map +1 -0
- package/dist/orchviz/session-discovery.d.ts +19 -0
- package/dist/orchviz/session-discovery.d.ts.map +1 -0
- package/dist/orchviz/session-discovery.js +104 -0
- package/dist/orchviz/session-discovery.js.map +1 -0
- package/dist/orchviz/session-manager.d.ts +17 -0
- package/dist/orchviz/session-manager.d.ts.map +1 -0
- package/dist/orchviz/session-manager.js +62 -0
- package/dist/orchviz/session-manager.js.map +1 -0
- package/dist/orchviz/session-runtime.d.ts +22 -0
- package/dist/orchviz/session-runtime.d.ts.map +1 -0
- package/dist/orchviz/session-runtime.js +135 -0
- package/dist/orchviz/session-runtime.js.map +1 -0
- package/dist/orchviz/session-watcher.d.ts +33 -0
- package/dist/orchviz/session-watcher.d.ts.map +1 -0
- package/dist/orchviz/session-watcher.js +135 -0
- package/dist/orchviz/session-watcher.js.map +1 -0
- package/dist/orchviz/subagent-meta.d.ts +8 -0
- package/dist/orchviz/subagent-meta.d.ts.map +1 -0
- package/dist/orchviz/subagent-meta.js +28 -0
- package/dist/orchviz/subagent-meta.js.map +1 -0
- package/dist/orchviz/subagent-scanner.d.ts +15 -0
- package/dist/orchviz/subagent-scanner.d.ts.map +1 -0
- package/dist/orchviz/subagent-scanner.js +27 -0
- package/dist/orchviz/subagent-scanner.js.map +1 -0
- package/dist/orchviz/subagent-watcher.d.ts +19 -0
- package/dist/orchviz/subagent-watcher.d.ts.map +1 -0
- package/dist/orchviz/subagent-watcher.js +147 -0
- package/dist/orchviz/subagent-watcher.js.map +1 -0
- package/dist/orchviz/test-server-boot.d.ts +28 -0
- package/dist/orchviz/test-server-boot.d.ts.map +1 -0
- package/dist/orchviz/test-server-boot.js +64 -0
- package/dist/orchviz/test-server-boot.js.map +1 -0
- package/dist/orchviz/token-estimator.d.ts +11 -0
- package/dist/orchviz/token-estimator.d.ts.map +1 -0
- package/dist/orchviz/token-estimator.js +38 -0
- package/dist/orchviz/token-estimator.js.map +1 -0
- package/dist/orchviz/tool-input-data.d.ts +11 -0
- package/dist/orchviz/tool-input-data.d.ts.map +1 -0
- package/dist/orchviz/tool-input-data.js +76 -0
- package/dist/orchviz/tool-input-data.js.map +1 -0
- package/dist/orchviz/tool-summarizer.d.ts +16 -0
- package/dist/orchviz/tool-summarizer.d.ts.map +1 -0
- package/dist/orchviz/tool-summarizer.js +131 -0
- package/dist/orchviz/tool-summarizer.js.map +1 -0
- package/dist/orchviz-web/index.css +1 -0
- package/dist/orchviz-web/index.html +18 -0
- package/dist/orchviz-web/index.js +51 -0
- package/package.json +18 -3
- package/dist/logger.d.ts +0 -26
- package/dist/logger.js +0 -70
- package/dist/logger.js.map +0 -1
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PlanCollector — read the active plan (or a slug-targeted plan) with mtime-keyed cache.
|
|
3
|
+
*
|
|
4
|
+
* Per red-team H3 (orchviz redesign): cache key is `(planDirMtime, max(phaseFileMtime))`.
|
|
5
|
+
* Cache invalidates the moment any plan file is touched on disk — no stale window.
|
|
6
|
+
* A 60s time-based fallback exists for paranoia (e.g., filesystems with broken mtime).
|
|
7
|
+
*
|
|
8
|
+
* Per R2-3: cache is plain Map<string, CacheEntry> keyed by slug-or-"__active".
|
|
9
|
+
* Soft-reset if map.size > 50. NO LRU, NO 16-entry cap, NO evict-oldest.
|
|
10
|
+
*
|
|
11
|
+
* Per R2-8: PlanSnapshot shape includes phaseEtags (not etag) and error tag.
|
|
12
|
+
* Read-only is the default behavior. Set MEOWKIT_ORCHVIZ_WRITABLE=1 to opt
|
|
13
|
+
* into write mode. Legacy MEOWKIT_ORCHVIZ_READONLY=0 continues to enable
|
|
14
|
+
* writes for backwards compatibility with existing user setups.
|
|
15
|
+
*/
|
|
16
|
+
import * as path from "node:path";
|
|
17
|
+
import * as fs from "node:fs";
|
|
18
|
+
import { findActivePlan } from "./find-active-plan.js";
|
|
19
|
+
import { planMtimeKey, readPlan } from "./index.js";
|
|
20
|
+
import { computeAllPhaseEtags } from "./etag.js";
|
|
21
|
+
import { SLUG_RE } from "./plan-constants.js";
|
|
22
|
+
import { createLogger } from "../logger.js";
|
|
23
|
+
const log = createLogger("PlanCollector");
|
|
24
|
+
const FALLBACK_TIMEOUT_MS = 60_000;
|
|
25
|
+
const CACHE_SOFT_RESET_THRESHOLD = 50;
|
|
26
|
+
const ACTIVE_KEY = "__active";
|
|
27
|
+
/**
|
|
28
|
+
* Read-only is the default. Precedence (highest first):
|
|
29
|
+
* 1. MEOWKIT_ORCHVIZ_READONLY=1 forces readonly (defensive lock)
|
|
30
|
+
* 2. MEOWKIT_ORCHVIZ_WRITABLE=1 opts into writes (preferred opt-in)
|
|
31
|
+
* 3. MEOWKIT_ORCHVIZ_READONLY=0 opts into writes (legacy compat)
|
|
32
|
+
* 4. otherwise readonly
|
|
33
|
+
*/
|
|
34
|
+
export function isOrchvizReadonly() {
|
|
35
|
+
if (process.env.MEOWKIT_ORCHVIZ_READONLY === "1")
|
|
36
|
+
return true;
|
|
37
|
+
if (process.env.MEOWKIT_ORCHVIZ_WRITABLE === "1")
|
|
38
|
+
return false;
|
|
39
|
+
if (process.env.MEOWKIT_ORCHVIZ_READONLY === "0")
|
|
40
|
+
return false;
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
export class PlanCollector {
|
|
44
|
+
projectRoot;
|
|
45
|
+
cache = new Map();
|
|
46
|
+
/** Cached `realpathSync(plansDir)` — constant per-projectRoot, avoids syscall per slug poll. */
|
|
47
|
+
resolvedPlansDir = null;
|
|
48
|
+
constructor(projectRoot) {
|
|
49
|
+
this.projectRoot = projectRoot;
|
|
50
|
+
}
|
|
51
|
+
snapshot(slug) {
|
|
52
|
+
const isReadonly = isOrchvizReadonly();
|
|
53
|
+
const generatedAt = new Date().toISOString();
|
|
54
|
+
if (slug !== undefined) {
|
|
55
|
+
return this.snapshotBySlug(slug, isReadonly, generatedAt);
|
|
56
|
+
}
|
|
57
|
+
return this.snapshotActive(isReadonly, generatedAt);
|
|
58
|
+
}
|
|
59
|
+
snapshotActive(isReadonly, generatedAt) {
|
|
60
|
+
const planDir = findActivePlan(this.projectRoot);
|
|
61
|
+
const mtimeKey = planDir ? planMtimeKey(planDir) : null;
|
|
62
|
+
const now = Date.now();
|
|
63
|
+
const cached = this.cache.get(ACTIVE_KEY);
|
|
64
|
+
if (cached &&
|
|
65
|
+
cached.mtimeKey === mtimeKey &&
|
|
66
|
+
now - cached.at < FALLBACK_TIMEOUT_MS) {
|
|
67
|
+
return { ...cached.snapshot, readonly: isReadonly, generatedAt };
|
|
68
|
+
}
|
|
69
|
+
const plan = planDir ? readPlan(planDir) : null;
|
|
70
|
+
const phaseEtags = planDir ? computeAllPhaseEtags(planDir) : null;
|
|
71
|
+
const snapshot = {
|
|
72
|
+
plan,
|
|
73
|
+
phaseEtags,
|
|
74
|
+
readonly: isReadonly,
|
|
75
|
+
generatedAt,
|
|
76
|
+
};
|
|
77
|
+
this.writeCache(ACTIVE_KEY, snapshot, mtimeKey, now);
|
|
78
|
+
return snapshot;
|
|
79
|
+
}
|
|
80
|
+
snapshotBySlug(slug, isReadonly, generatedAt) {
|
|
81
|
+
// Validate slug regex BEFORE any path resolution (defense vs traversal)
|
|
82
|
+
if (!SLUG_RE.test(slug)) {
|
|
83
|
+
return {
|
|
84
|
+
plan: null,
|
|
85
|
+
phaseEtags: null,
|
|
86
|
+
readonly: isReadonly,
|
|
87
|
+
generatedAt,
|
|
88
|
+
error: "invalid-slug",
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
const plansDir = path.join(this.projectRoot, "tasks", "plans");
|
|
92
|
+
const planDir = path.join(plansDir, slug);
|
|
93
|
+
// Boundary check via realpath — cached per-process (constant per projectRoot).
|
|
94
|
+
// macOS /var→/private/var symlink resolves once.
|
|
95
|
+
if (this.resolvedPlansDir === null) {
|
|
96
|
+
try {
|
|
97
|
+
this.resolvedPlansDir = fs.realpathSync(plansDir);
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return {
|
|
101
|
+
plan: null,
|
|
102
|
+
phaseEtags: null,
|
|
103
|
+
readonly: isReadonly,
|
|
104
|
+
generatedAt,
|
|
105
|
+
error: "not-found",
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
const boundary = this.resolvedPlansDir + path.sep;
|
|
110
|
+
let resolved;
|
|
111
|
+
try {
|
|
112
|
+
resolved = fs.realpathSync(planDir);
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
return {
|
|
116
|
+
plan: null,
|
|
117
|
+
phaseEtags: null,
|
|
118
|
+
readonly: isReadonly,
|
|
119
|
+
generatedAt,
|
|
120
|
+
error: "not-found",
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
if (!(resolved + path.sep).startsWith(boundary)) {
|
|
124
|
+
log.warn(`slug boundary violation: ${slug} -> ${resolved}`);
|
|
125
|
+
return {
|
|
126
|
+
plan: null,
|
|
127
|
+
phaseEtags: null,
|
|
128
|
+
readonly: isReadonly,
|
|
129
|
+
generatedAt,
|
|
130
|
+
error: "forbidden-path",
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
const mtimeKey = planMtimeKey(planDir);
|
|
134
|
+
const now = Date.now();
|
|
135
|
+
const cached = this.cache.get(slug);
|
|
136
|
+
if (cached &&
|
|
137
|
+
cached.mtimeKey === mtimeKey &&
|
|
138
|
+
now - cached.at < FALLBACK_TIMEOUT_MS) {
|
|
139
|
+
return { ...cached.snapshot, readonly: isReadonly, generatedAt };
|
|
140
|
+
}
|
|
141
|
+
const plan = readPlan(planDir);
|
|
142
|
+
if (!plan) {
|
|
143
|
+
return {
|
|
144
|
+
plan: null,
|
|
145
|
+
phaseEtags: null,
|
|
146
|
+
readonly: isReadonly,
|
|
147
|
+
generatedAt,
|
|
148
|
+
error: "not-found",
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
const phaseEtags = computeAllPhaseEtags(planDir);
|
|
152
|
+
const snapshot = {
|
|
153
|
+
plan,
|
|
154
|
+
phaseEtags,
|
|
155
|
+
readonly: isReadonly,
|
|
156
|
+
generatedAt,
|
|
157
|
+
};
|
|
158
|
+
this.writeCache(slug, snapshot, mtimeKey, now);
|
|
159
|
+
return snapshot;
|
|
160
|
+
}
|
|
161
|
+
/** Soft-reset the cache if it grows too large, then insert the new entry (R2-3). */
|
|
162
|
+
writeCache(key, snapshot, mtimeKey, now) {
|
|
163
|
+
if (this.cache.size >= CACHE_SOFT_RESET_THRESHOLD) {
|
|
164
|
+
this.cache.clear();
|
|
165
|
+
}
|
|
166
|
+
this.cache.set(key, { snapshot, mtimeKey, at: now });
|
|
167
|
+
}
|
|
168
|
+
invalidate() {
|
|
169
|
+
this.cache.clear();
|
|
170
|
+
// resolvedPlansDir is per-projectRoot and never changes; do NOT clear.
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=collector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collector.js","sourceRoot":"","sources":["../../../src/orchviz/plan/collector.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ETag helpers — per-phase-file sha256 hash.
|
|
3
|
+
*
|
|
4
|
+
* Per red-team H2 / R2-2: ETag scope is per-FILE, NOT a bundle hash.
|
|
5
|
+
* Concurrent writes to different phases do not produce false 409s.
|
|
6
|
+
*
|
|
7
|
+
* computePhaseFileEtag(phaseFilePath) → 64-char lowercase hex sha256.
|
|
8
|
+
* Missing file → "" (not an error; caller decides how to handle absent files).
|
|
9
|
+
*
|
|
10
|
+
* computeAllPhaseEtags(planDir) → Record<phaseNumber, hex64>
|
|
11
|
+
* for every phase-NN-*.md in the directory.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Compute sha256 of a single phase file's bytes.
|
|
15
|
+
* Returns 64-char lowercase hex, or "" if the file cannot be read.
|
|
16
|
+
*/
|
|
17
|
+
export declare function computePhaseFileEtag(phaseFilePath: string): string;
|
|
18
|
+
/**
|
|
19
|
+
* Compute etags for every phase-NN-*.md file in planDir.
|
|
20
|
+
* Returns a Record keyed by phase number (integer).
|
|
21
|
+
* Missing or unreadable files are omitted.
|
|
22
|
+
*/
|
|
23
|
+
export declare function computeAllPhaseEtags(planDir: string): Record<number, string>;
|
|
24
|
+
//# sourceMappingURL=etag.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"etag.d.ts","sourceRoot":"","sources":["../../../src/orchviz/plan/etag.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAOH;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAQlE;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAmB5E"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ETag helpers — per-phase-file sha256 hash.
|
|
3
|
+
*
|
|
4
|
+
* Per red-team H2 / R2-2: ETag scope is per-FILE, NOT a bundle hash.
|
|
5
|
+
* Concurrent writes to different phases do not produce false 409s.
|
|
6
|
+
*
|
|
7
|
+
* computePhaseFileEtag(phaseFilePath) → 64-char lowercase hex sha256.
|
|
8
|
+
* Missing file → "" (not an error; caller decides how to handle absent files).
|
|
9
|
+
*
|
|
10
|
+
* computeAllPhaseEtags(planDir) → Record<phaseNumber, hex64>
|
|
11
|
+
* for every phase-NN-*.md in the directory.
|
|
12
|
+
*/
|
|
13
|
+
import * as crypto from "node:crypto";
|
|
14
|
+
import * as fs from "node:fs";
|
|
15
|
+
import * as path from "node:path";
|
|
16
|
+
import { PHASE_FILE_NUM_RE } from "./plan-constants.js";
|
|
17
|
+
/**
|
|
18
|
+
* Compute sha256 of a single phase file's bytes.
|
|
19
|
+
* Returns 64-char lowercase hex, or "" if the file cannot be read.
|
|
20
|
+
*/
|
|
21
|
+
export function computePhaseFileEtag(phaseFilePath) {
|
|
22
|
+
let buf;
|
|
23
|
+
try {
|
|
24
|
+
buf = fs.readFileSync(phaseFilePath);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return "";
|
|
28
|
+
}
|
|
29
|
+
return crypto.createHash("sha256").update(buf).digest("hex");
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Compute etags for every phase-NN-*.md file in planDir.
|
|
33
|
+
* Returns a Record keyed by phase number (integer).
|
|
34
|
+
* Missing or unreadable files are omitted.
|
|
35
|
+
*/
|
|
36
|
+
export function computeAllPhaseEtags(planDir) {
|
|
37
|
+
let entries;
|
|
38
|
+
try {
|
|
39
|
+
entries = fs.readdirSync(planDir);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return {};
|
|
43
|
+
}
|
|
44
|
+
const out = {};
|
|
45
|
+
for (const name of entries) {
|
|
46
|
+
const m = name.match(PHASE_FILE_NUM_RE);
|
|
47
|
+
if (!m)
|
|
48
|
+
continue;
|
|
49
|
+
const phaseNum = parseInt(m[1], 10);
|
|
50
|
+
const filePath = path.join(planDir, name);
|
|
51
|
+
const etag = computePhaseFileEtag(filePath);
|
|
52
|
+
if (etag !== "") {
|
|
53
|
+
out[phaseNum] = etag;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return out;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=etag.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"etag.js","sourceRoot":"","sources":["../../../src/orchviz/plan/etag.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AACtC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAAC,aAAqB;IACzD,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACJ,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,aAAa,CAAC,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,EAAE,CAAC;IACX,CAAC;IACD,OAAO,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC9D,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAAC,OAAe;IACnD,IAAI,OAAiB,CAAC;IACtB,IAAI,CAAC;QACJ,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IACnC,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,EAAE,CAAC;IACX,CAAC;IACD,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;QACxC,IAAI,CAAC,CAAC;YAAE,SAAS;QACjB,MAAM,QAAQ,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACpC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QAC1C,MAAM,IAAI,GAAG,oBAAoB,CAAC,QAAQ,CAAC,CAAC;QAC5C,IAAI,IAAI,KAAK,EAAE,EAAE,CAAC;YACjB,GAAG,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC;QACtB,CAAC;IACF,CAAC;IACD,OAAO,GAAG,CAAC;AACZ,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* findActivePlan — discover the most-recent non-archived plan dir.
|
|
3
|
+
*
|
|
4
|
+
* Per red-team C1 (orchviz redesign plan): every candidate path is realpath-resolved
|
|
5
|
+
* and asserted to live inside `${projectRoot}/tasks/plans/`. Symlinks pointing
|
|
6
|
+
* outside the boundary are rejected to prevent path-disclosure via /api/plan.
|
|
7
|
+
*/
|
|
8
|
+
export declare function findActivePlan(projectRoot: string): string | null;
|
|
9
|
+
//# sourceMappingURL=find-active-plan.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"find-active-plan.d.ts","sourceRoot":"","sources":["../../../src/orchviz/plan/find-active-plan.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAeH,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAiDjE"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* findActivePlan — discover the most-recent non-archived plan dir.
|
|
3
|
+
*
|
|
4
|
+
* Per red-team C1 (orchviz redesign plan): every candidate path is realpath-resolved
|
|
5
|
+
* and asserted to live inside `${projectRoot}/tasks/plans/`. Symlinks pointing
|
|
6
|
+
* outside the boundary are rejected to prevent path-disclosure via /api/plan.
|
|
7
|
+
*/
|
|
8
|
+
import * as fs from "node:fs";
|
|
9
|
+
import * as path from "node:path";
|
|
10
|
+
import yaml from "js-yaml";
|
|
11
|
+
import { FRONTMATTER_RE } from "./plan-constants.js";
|
|
12
|
+
import { createLogger } from "../logger.js";
|
|
13
|
+
const log = createLogger("PlanFinder");
|
|
14
|
+
export function findActivePlan(projectRoot) {
|
|
15
|
+
const plansDir = path.join(projectRoot, "tasks", "plans");
|
|
16
|
+
let resolvedPlansDir;
|
|
17
|
+
try {
|
|
18
|
+
resolvedPlansDir = fs.realpathSync(plansDir);
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
const boundary = resolvedPlansDir + path.sep;
|
|
24
|
+
let entries;
|
|
25
|
+
try {
|
|
26
|
+
entries = fs.readdirSync(plansDir, { withFileTypes: true });
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
const candidates = [];
|
|
32
|
+
for (const entry of entries) {
|
|
33
|
+
if (!entry.isDirectory())
|
|
34
|
+
continue;
|
|
35
|
+
const dir = path.join(plansDir, entry.name);
|
|
36
|
+
const planFile = path.join(dir, "plan.md");
|
|
37
|
+
// Boundary check (red-team C1)
|
|
38
|
+
let resolved;
|
|
39
|
+
try {
|
|
40
|
+
resolved = fs.realpathSync(dir);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
if (!(resolved + path.sep).startsWith(boundary)) {
|
|
46
|
+
log.warn(`rejecting plan dir outside boundary: ${dir} -> ${resolved}`);
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
let stat;
|
|
50
|
+
try {
|
|
51
|
+
stat = fs.statSync(planFile);
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
if (!stat.isFile())
|
|
57
|
+
continue;
|
|
58
|
+
if (isArchived(planFile))
|
|
59
|
+
continue;
|
|
60
|
+
candidates.push({ dir, mtimeMs: stat.mtimeMs });
|
|
61
|
+
}
|
|
62
|
+
if (candidates.length === 0)
|
|
63
|
+
return null;
|
|
64
|
+
candidates.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
65
|
+
return candidates[0].dir;
|
|
66
|
+
}
|
|
67
|
+
function isArchived(planFile) {
|
|
68
|
+
let raw;
|
|
69
|
+
try {
|
|
70
|
+
raw = fs.readFileSync(planFile, "utf-8");
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
const fmMatch = raw.match(FRONTMATTER_RE);
|
|
76
|
+
if (!fmMatch)
|
|
77
|
+
return false;
|
|
78
|
+
let parsed;
|
|
79
|
+
try {
|
|
80
|
+
parsed = yaml.load(fmMatch[1], { schema: yaml.FAILSAFE_SCHEMA });
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
if (!parsed || typeof parsed !== "object")
|
|
86
|
+
return false;
|
|
87
|
+
const status = parsed.status;
|
|
88
|
+
return typeof status === "string" && status.toLowerCase() === "archived";
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=find-active-plan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"find-active-plan.js","sourceRoot":"","sources":["../../../src/orchviz/plan/find-active-plan.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* orchviz plan parser — orchestrator for plan + phase files.
|
|
3
|
+
*
|
|
4
|
+
* Public API:
|
|
5
|
+
* - findActivePlan(projectRoot)
|
|
6
|
+
* - readPlan(planDir)
|
|
7
|
+
*
|
|
8
|
+
* Read-only by design. v1.1 does not write to plan files.
|
|
9
|
+
*/
|
|
10
|
+
import type { PlanState } from "./types.js";
|
|
11
|
+
export type { PhaseState, PhaseStatus, PlanState, PlanStatus, PlanSummary, TodoItem } from "./types.js";
|
|
12
|
+
export { findActivePlan } from "./find-active-plan.js";
|
|
13
|
+
export { parsePlanFile, type PlanScaffold } from "./parse-plan-file.js";
|
|
14
|
+
export { parsePhaseFile } from "./parse-phase-file.js";
|
|
15
|
+
export { listPlans } from "./list-plans.js";
|
|
16
|
+
export { computePhaseFileEtag, computeAllPhaseEtags } from "./etag.js";
|
|
17
|
+
export declare function readPlan(planDir: string): PlanState | null;
|
|
18
|
+
/**
|
|
19
|
+
* Compute a cache key from filesystem mtimes (red-team H3).
|
|
20
|
+
* Returns a string like "1730403600000:1730403700000" or null if planDir gone.
|
|
21
|
+
*/
|
|
22
|
+
export declare function planMtimeKey(planDir: string): string | null;
|
|
23
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/orchviz/plan/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAc,SAAS,EAAE,MAAM,YAAY,CAAC;AAGxD,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACxG,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,KAAK,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AAQvE,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CA6C1D;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAa3D"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* orchviz plan parser — orchestrator for plan + phase files.
|
|
3
|
+
*
|
|
4
|
+
* Public API:
|
|
5
|
+
* - findActivePlan(projectRoot)
|
|
6
|
+
* - readPlan(planDir)
|
|
7
|
+
*
|
|
8
|
+
* Read-only by design. v1.1 does not write to plan files.
|
|
9
|
+
*/
|
|
10
|
+
import * as fs from "node:fs";
|
|
11
|
+
import * as path from "node:path";
|
|
12
|
+
import { PHASE_FILE_RE } from "./plan-constants.js";
|
|
13
|
+
export { findActivePlan } from "./find-active-plan.js";
|
|
14
|
+
export { parsePlanFile } from "./parse-plan-file.js";
|
|
15
|
+
export { parsePhaseFile } from "./parse-phase-file.js";
|
|
16
|
+
export { listPlans } from "./list-plans.js";
|
|
17
|
+
export { computePhaseFileEtag, computeAllPhaseEtags } from "./etag.js";
|
|
18
|
+
import { parsePlanFile } from "./parse-plan-file.js";
|
|
19
|
+
import { parsePhaseFile } from "./parse-phase-file.js";
|
|
20
|
+
import { createLogger } from "../logger.js";
|
|
21
|
+
const log = createLogger("PlanReader");
|
|
22
|
+
export function readPlan(planDir) {
|
|
23
|
+
const scaffold = parsePlanFile(planDir);
|
|
24
|
+
if (!scaffold)
|
|
25
|
+
return null;
|
|
26
|
+
// Use realpathSync for boundary so macOS /var→/private/var symlink doesn't break check
|
|
27
|
+
let resolvedPlanDir;
|
|
28
|
+
try {
|
|
29
|
+
resolvedPlanDir = fs.realpathSync(planDir);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
const boundary = resolvedPlanDir + path.sep;
|
|
35
|
+
let entries;
|
|
36
|
+
try {
|
|
37
|
+
entries = fs.readdirSync(planDir);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
const phaseFiles = [];
|
|
43
|
+
for (const name of entries) {
|
|
44
|
+
if (!PHASE_FILE_RE.test(name))
|
|
45
|
+
continue;
|
|
46
|
+
const full = path.join(planDir, name);
|
|
47
|
+
// Boundary check (red-team C1) — defense in depth even though planDir already checked.
|
|
48
|
+
let resolved;
|
|
49
|
+
try {
|
|
50
|
+
resolved = fs.realpathSync(full);
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (!resolved.startsWith(boundary)) {
|
|
56
|
+
log.warn(`rejecting phase file outside boundary: ${full}`);
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
phaseFiles.push(full);
|
|
60
|
+
}
|
|
61
|
+
const phases = [];
|
|
62
|
+
for (const filePath of phaseFiles) {
|
|
63
|
+
const phase = parsePhaseFile(filePath);
|
|
64
|
+
if (phase)
|
|
65
|
+
phases.push(phase);
|
|
66
|
+
}
|
|
67
|
+
phases.sort((a, b) => a.number - b.number);
|
|
68
|
+
return { ...scaffold, phases };
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Compute a cache key from filesystem mtimes (red-team H3).
|
|
72
|
+
* Returns a string like "1730403600000:1730403700000" or null if planDir gone.
|
|
73
|
+
*/
|
|
74
|
+
export function planMtimeKey(planDir) {
|
|
75
|
+
try {
|
|
76
|
+
const planStat = fs.statSync(path.join(planDir, "plan.md"));
|
|
77
|
+
let maxPhaseMtime = 0;
|
|
78
|
+
for (const name of fs.readdirSync(planDir)) {
|
|
79
|
+
if (!PHASE_FILE_RE.test(name))
|
|
80
|
+
continue;
|
|
81
|
+
const stat = fs.statSync(path.join(planDir, name));
|
|
82
|
+
if (stat.mtimeMs > maxPhaseMtime)
|
|
83
|
+
maxPhaseMtime = stat.mtimeMs;
|
|
84
|
+
}
|
|
85
|
+
return `${planStat.mtimeMs}:${maxPhaseMtime}`;
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/orchviz/plan/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* listPlans — enumerate all non-archived plan dirs in tasks/plans/.
|
|
3
|
+
*
|
|
4
|
+
* Per red-team SEC#6: realpath boundary check is applied to EVERY subdir before
|
|
5
|
+
* any file inside is read. This mirrors the check in findActivePlan.
|
|
6
|
+
*
|
|
7
|
+
* Returns up to `limit` (default 100) PlanSummary objects sorted by plan.md
|
|
8
|
+
* mtimeMs descending (most recently modified first).
|
|
9
|
+
*/
|
|
10
|
+
import type { PlanSummary } from "./types.js";
|
|
11
|
+
export interface ListPlansOptions {
|
|
12
|
+
limit?: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* List all non-archived plans in projectRoot/tasks/plans/.
|
|
16
|
+
* Each candidate subdir is realpath-checked against the boundary before read.
|
|
17
|
+
*/
|
|
18
|
+
export declare function listPlans(projectRoot: string, opts?: ListPlansOptions): PlanSummary[];
|
|
19
|
+
//# sourceMappingURL=list-plans.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-plans.d.ts","sourceRoot":"","sources":["../../../src/orchviz/plan/list-plans.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,KAAK,EAAc,WAAW,EAAE,MAAM,YAAY,CAAC;AAoE1D,MAAM,WAAW,gBAAgB;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,GAAE,gBAAqB,GAAG,WAAW,EAAE,CAyEzF"}
|