@cleocode/cleo 2026.2.9 → 2026.3.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 +14 -13
- package/dist/cli/index.js +1920 -1180
- package/dist/cli/index.js.map +4 -4
- package/dist/mcp/index.js +1645 -793
- package/dist/mcp/index.js.map +4 -4
- package/drizzle/20260301053344_careless_changeling/migration.sql +27 -0
- package/drizzle/20260301053344_careless_changeling/snapshot.json +2598 -0
- package/package.json +2 -2
- package/packages/ct-skills/skills/ct-cleo/SKILL.md +49 -22
- package/templates/CLEO-INJECTION.md +32 -138
- package/templates/cleo-gitignore +66 -49
- package/templates/git-hooks/pre-commit +24 -6
- package/schemas/archive/agent-configs.schema.json +0 -120
- package/schemas/archive/agent-registry.schema.json +0 -132
- package/schemas/archive/archive.schema.json +0 -450
- package/schemas/archive/claudedocs-frontmatter.schema.json +0 -162
- package/schemas/archive/commands-index.schema.json +0 -158
- package/schemas/archive/contribution.schema.json +0 -722
- package/schemas/archive/critical-path.schema.json +0 -246
- package/schemas/archive/deps-cache.schema.json +0 -97
- package/schemas/archive/doctor-output.schema.json +0 -283
- package/schemas/archive/error.schema.json +0 -161
- package/schemas/archive/export-package.schema.json +0 -375
- package/schemas/archive/global-config.schema.json +0 -219
- package/schemas/archive/log.schema.json +0 -250
- package/schemas/archive/metrics.schema.json +0 -328
- package/schemas/archive/migrations.schema.json +0 -150
- package/schemas/archive/nexus-registry.schema.json +0 -90
- package/schemas/archive/output.schema.json +0 -164
- package/schemas/archive/rcsd-consensus-report.schema.json +0 -491
- package/schemas/archive/rcsd-hitl-resolution.schema.json +0 -216
- package/schemas/archive/rcsd-index.schema.json +0 -384
- package/schemas/archive/rcsd-manifest.schema.json +0 -264
- package/schemas/archive/rcsd-research-output.schema.json +0 -564
- package/schemas/archive/rcsd-spec-frontmatter.schema.json +0 -225
- package/schemas/archive/releases.schema.json +0 -267
- package/schemas/archive/skills-manifest.schema.json +0 -91
- package/schemas/archive/skillsmp.schema.json +0 -208
- package/schemas/archive/spec-index.schema.json +0 -196
- package/schemas/archive/todo.schema.json +0 -995
- package/schemas/claudedocs-frontmatter.schema.json +0 -162
- package/schemas/commands-index.schema.json +0 -158
- package/schemas/rcsd-consensus-report.schema.json +0 -494
- package/schemas/rcsd-hitl-resolution.schema.json +0 -219
- package/schemas/rcsd-index.schema.json +0 -387
- package/schemas/rcsd-manifest.schema.json +0 -267
- package/schemas/rcsd-research-output.schema.json +0 -567
- package/schemas/rcsd-spec-frontmatter.schema.json +0 -225
- package/schemas/todo.schema.json +0 -994
- package/skills/_shared/cleo-style-guide.md +0 -84
- package/skills/_shared/manifest-operations.md +0 -810
- package/skills/_shared/placeholders.json +0 -433
- package/skills/_shared/skill-chaining-patterns.md +0 -240
- package/skills/_shared/subagent-protocol-base.md +0 -221
- package/skills/_shared/task-system-integration.md +0 -232
- package/skills/_shared/testing-framework-config.md +0 -110
- package/skills/agentskills-integrate.md +0 -104
- package/skills/agentskills-specs.md +0 -255
- package/skills/agentskills-what-are-skills.md +0 -75
- package/skills/manifest.json +0 -510
- package/templates/AGENT-INJECTION.md +0 -166
- /package/schemas/{research-manifest.schema.json → archive/research-manifest.schema.json} +0 -0
package/dist/mcp/index.js
CHANGED
|
@@ -501,6 +501,7 @@ __export(schema_exports, {
|
|
|
501
501
|
lifecyclePipelines: () => lifecyclePipelines,
|
|
502
502
|
lifecycleStages: () => lifecycleStages,
|
|
503
503
|
lifecycleTransitions: () => lifecycleTransitions,
|
|
504
|
+
manifestEntries: () => manifestEntries,
|
|
504
505
|
schemaMeta: () => schemaMeta,
|
|
505
506
|
sessions: () => sessions,
|
|
506
507
|
statusRegistryTable: () => statusRegistryTable,
|
|
@@ -517,7 +518,7 @@ import {
|
|
|
517
518
|
primaryKey
|
|
518
519
|
} from "drizzle-orm/sqlite-core";
|
|
519
520
|
import { sql } from "drizzle-orm";
|
|
520
|
-
var TASK_PRIORITIES, TASK_TYPES, TASK_SIZES, LIFECYCLE_STAGE_NAMES, LIFECYCLE_GATE_RESULTS, LIFECYCLE_EVIDENCE_TYPES, tasks, taskDependencies, taskRelations, sessions, taskWorkHistory, lifecyclePipelines, lifecycleStages, lifecycleGateResults, lifecycleEvidence, lifecycleTransitions, schemaMeta, auditLog, architectureDecisions, adrTaskLinks, adrRelations, statusRegistryTable;
|
|
521
|
+
var TASK_PRIORITIES, TASK_TYPES, TASK_SIZES, LIFECYCLE_STAGE_NAMES, LIFECYCLE_GATE_RESULTS, LIFECYCLE_EVIDENCE_TYPES, tasks, taskDependencies, taskRelations, sessions, taskWorkHistory, lifecyclePipelines, lifecycleStages, lifecycleGateResults, lifecycleEvidence, lifecycleTransitions, manifestEntries, schemaMeta, auditLog, architectureDecisions, adrTaskLinks, adrRelations, statusRegistryTable;
|
|
521
522
|
var init_schema = __esm({
|
|
522
523
|
"src/store/schema.ts"() {
|
|
523
524
|
"use strict";
|
|
@@ -613,8 +614,9 @@ var init_schema = __esm({
|
|
|
613
614
|
taskId: text("task_id").notNull().references(() => tasks.id, { onDelete: "cascade" }),
|
|
614
615
|
relatedTo: text("related_to").notNull().references(() => tasks.id, { onDelete: "cascade" }),
|
|
615
616
|
relationType: text("relation_type", {
|
|
616
|
-
enum: ["related", "blocks", "duplicates"]
|
|
617
|
-
}).notNull().default("related")
|
|
617
|
+
enum: ["related", "blocks", "duplicates", "absorbs", "fixes", "extends", "supersedes"]
|
|
618
|
+
}).notNull().default("related"),
|
|
619
|
+
reason: text("reason")
|
|
618
620
|
}, (table) => [
|
|
619
621
|
primaryKey({ columns: [table.taskId, table.relatedTo] })
|
|
620
622
|
]);
|
|
@@ -687,7 +689,16 @@ var init_schema = __esm({
|
|
|
687
689
|
skippedAt: text("skipped_at"),
|
|
688
690
|
skipReason: text("skip_reason"),
|
|
689
691
|
notesJson: text("notes_json").default("[]"),
|
|
690
|
-
metadataJson: text("metadata_json").default("{}")
|
|
692
|
+
metadataJson: text("metadata_json").default("{}"),
|
|
693
|
+
// RCASD provenance tracking columns (T5100)
|
|
694
|
+
outputFile: text("output_file"),
|
|
695
|
+
createdBy: text("created_by"),
|
|
696
|
+
validatedBy: text("validated_by"),
|
|
697
|
+
validatedAt: text("validated_at"),
|
|
698
|
+
validationStatus: text("validation_status", {
|
|
699
|
+
enum: ["pending", "in_review", "approved", "rejected", "needs_revision"]
|
|
700
|
+
}),
|
|
701
|
+
provenanceChainJson: text("provenance_chain_json")
|
|
691
702
|
}, (table) => [
|
|
692
703
|
index("idx_lifecycle_stages_pipeline_id").on(table.pipelineId),
|
|
693
704
|
index("idx_lifecycle_stages_stage_name").on(table.stageName),
|
|
@@ -732,6 +743,25 @@ var init_schema = __esm({
|
|
|
732
743
|
}, (table) => [
|
|
733
744
|
index("idx_lifecycle_transitions_pipeline_id").on(table.pipelineId)
|
|
734
745
|
]);
|
|
746
|
+
manifestEntries = sqliteTable("manifest_entries", {
|
|
747
|
+
id: text("id").primaryKey(),
|
|
748
|
+
pipelineId: text("pipeline_id").references(() => lifecyclePipelines.id, { onDelete: "cascade" }),
|
|
749
|
+
stageId: text("stage_id").references(() => lifecycleStages.id, { onDelete: "cascade" }),
|
|
750
|
+
title: text("title").notNull(),
|
|
751
|
+
date: text("date").notNull(),
|
|
752
|
+
status: text("status", { enum: MANIFEST_STATUSES }).notNull(),
|
|
753
|
+
agentType: text("agent_type"),
|
|
754
|
+
outputFile: text("output_file"),
|
|
755
|
+
topicsJson: text("topics_json").default("[]"),
|
|
756
|
+
findingsJson: text("findings_json").default("[]"),
|
|
757
|
+
linkedTasksJson: text("linked_tasks_json").default("[]"),
|
|
758
|
+
createdBy: text("created_by"),
|
|
759
|
+
createdAt: text("created_at").notNull().default(sql`(datetime('now'))`)
|
|
760
|
+
}, (table) => [
|
|
761
|
+
index("idx_manifest_entries_pipeline_id").on(table.pipelineId),
|
|
762
|
+
index("idx_manifest_entries_stage_id").on(table.stageId),
|
|
763
|
+
index("idx_manifest_entries_status").on(table.status)
|
|
764
|
+
]);
|
|
735
765
|
schemaMeta = sqliteTable("schema_meta", {
|
|
736
766
|
key: text("key").primaryKey(),
|
|
737
767
|
value: text("value").notNull()
|
|
@@ -829,11 +859,36 @@ function openNativeDatabase(path, options) {
|
|
|
829
859
|
readOnly: options?.readonly ?? false,
|
|
830
860
|
timeout: options?.timeout ?? 5e3
|
|
831
861
|
});
|
|
862
|
+
db.exec("PRAGMA busy_timeout=5000");
|
|
832
863
|
if (options?.enableWal !== false) {
|
|
833
|
-
|
|
864
|
+
const MAX_WAL_RETRIES = 3;
|
|
865
|
+
const RETRY_DELAY_MS = 200;
|
|
866
|
+
let walSet = false;
|
|
867
|
+
for (let attempt = 1; attempt <= MAX_WAL_RETRIES; attempt++) {
|
|
868
|
+
db.exec("PRAGMA journal_mode=WAL");
|
|
869
|
+
const result = db.prepare("PRAGMA journal_mode").get();
|
|
870
|
+
const currentMode = result?.journal_mode?.toLowerCase?.() ?? "unknown";
|
|
871
|
+
if (currentMode === "wal") {
|
|
872
|
+
walSet = true;
|
|
873
|
+
break;
|
|
874
|
+
}
|
|
875
|
+
if (attempt < MAX_WAL_RETRIES) {
|
|
876
|
+
const buf = new SharedArrayBuffer(4);
|
|
877
|
+
Atomics.wait(new Int32Array(buf), 0, 0, RETRY_DELAY_MS * attempt);
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
if (!walSet) {
|
|
881
|
+
const finalResult = db.prepare("PRAGMA journal_mode").get();
|
|
882
|
+
const finalMode = finalResult?.journal_mode?.toLowerCase?.() ?? "unknown";
|
|
883
|
+
if (finalMode !== "wal") {
|
|
884
|
+
db.close();
|
|
885
|
+
throw new Error(
|
|
886
|
+
`CRITICAL: Failed to set WAL journal mode after ${MAX_WAL_RETRIES} attempts. Database is in '${finalMode}' mode. Another process likely holds an EXCLUSIVE lock on ${path}. Refusing to open \u2014 concurrent writes in DELETE mode cause data loss. Kill other cleo/MCP processes and retry. (T5173)`
|
|
887
|
+
);
|
|
888
|
+
}
|
|
889
|
+
}
|
|
834
890
|
}
|
|
835
891
|
db.exec("PRAGMA foreign_keys=ON");
|
|
836
|
-
db.exec("PRAGMA busy_timeout=5000");
|
|
837
892
|
return db;
|
|
838
893
|
}
|
|
839
894
|
function createDrizzleCallback(db) {
|
|
@@ -894,13 +949,14 @@ __export(sqlite_exports, {
|
|
|
894
949
|
getDbPath: () => getDbPath,
|
|
895
950
|
getNativeDb: () => getNativeDb,
|
|
896
951
|
getSchemaVersion: () => getSchemaVersion,
|
|
952
|
+
isSqliteBusy: () => isSqliteBusy,
|
|
897
953
|
resetDbState: () => resetDbState,
|
|
898
954
|
resolveMigrationsFolder: () => resolveMigrationsFolder,
|
|
899
955
|
schema: () => schema_exports
|
|
900
956
|
});
|
|
901
957
|
import { existsSync as existsSync3, mkdirSync } from "node:fs";
|
|
902
958
|
import { createRequire as createRequire2 } from "node:module";
|
|
903
|
-
import { dirname as dirname2, join as join3 } from "node:path";
|
|
959
|
+
import { dirname as dirname2, join as join3, resolve as resolve3 } from "node:path";
|
|
904
960
|
import { fileURLToPath } from "node:url";
|
|
905
961
|
import { eq } from "drizzle-orm";
|
|
906
962
|
import { readMigrationFiles } from "drizzle-orm/migrator";
|
|
@@ -932,6 +988,22 @@ async function getDb(cwd) {
|
|
|
932
988
|
nativeDb.exec(
|
|
933
989
|
`INSERT OR IGNORE INTO schema_meta (key, value) VALUES ('task_id_sequence', '{"counter":0,"lastId":"T000","checksum":"seed"}')`
|
|
934
990
|
);
|
|
991
|
+
if (!_gitTrackingChecked) {
|
|
992
|
+
_gitTrackingChecked = true;
|
|
993
|
+
try {
|
|
994
|
+
const { execFileSync: execFileSync6 } = await import("node:child_process");
|
|
995
|
+
execFileSync6("git", ["ls-files", "--error-unmatch", dbPath], {
|
|
996
|
+
cwd: resolve3(dbPath, "..", ".."),
|
|
997
|
+
stdio: "pipe"
|
|
998
|
+
});
|
|
999
|
+
const log5 = getLogger("sqlite");
|
|
1000
|
+
log5.warn(
|
|
1001
|
+
{ dbPath },
|
|
1002
|
+
"tasks.db is tracked by project git \u2014 this risks data loss on git operations. Run: git rm --cached .cleo/tasks.db (see ADR-013)"
|
|
1003
|
+
);
|
|
1004
|
+
} catch {
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
935
1007
|
_db = db;
|
|
936
1008
|
return db;
|
|
937
1009
|
})();
|
|
@@ -952,6 +1024,11 @@ function tableExists(nativeDb, tableName) {
|
|
|
952
1024
|
).get(tableName);
|
|
953
1025
|
return !!result;
|
|
954
1026
|
}
|
|
1027
|
+
function isSqliteBusy(err) {
|
|
1028
|
+
if (!(err instanceof Error)) return false;
|
|
1029
|
+
const msg = err.message.toLowerCase();
|
|
1030
|
+
return msg.includes("sqlite_busy") || msg.includes("database is locked");
|
|
1031
|
+
}
|
|
955
1032
|
async function runMigrations(nativeDb, db) {
|
|
956
1033
|
const migrationsFolder = resolveMigrationsFolder();
|
|
957
1034
|
if (tableExists(nativeDb, "tasks") && !tableExists(nativeDb, "__drizzle_migrations")) {
|
|
@@ -971,16 +1048,33 @@ async function runMigrations(nativeDb, db) {
|
|
|
971
1048
|
}
|
|
972
1049
|
}
|
|
973
1050
|
await migrate(db, async (queries) => {
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
nativeDb.prepare(
|
|
1051
|
+
let lastError;
|
|
1052
|
+
for (let attempt = 1; attempt <= MAX_MIGRATION_RETRIES; attempt++) {
|
|
1053
|
+
try {
|
|
1054
|
+
nativeDb.prepare("BEGIN IMMEDIATE").run();
|
|
1055
|
+
try {
|
|
1056
|
+
for (const query of queries) {
|
|
1057
|
+
nativeDb.prepare(query).run();
|
|
1058
|
+
}
|
|
1059
|
+
nativeDb.prepare("COMMIT").run();
|
|
1060
|
+
return;
|
|
1061
|
+
} catch (err) {
|
|
1062
|
+
nativeDb.prepare("ROLLBACK").run();
|
|
1063
|
+
throw err;
|
|
1064
|
+
}
|
|
1065
|
+
} catch (err) {
|
|
1066
|
+
if (!isSqliteBusy(err) || attempt === MAX_MIGRATION_RETRIES) {
|
|
1067
|
+
throw err;
|
|
1068
|
+
}
|
|
1069
|
+
lastError = err;
|
|
1070
|
+
const exponentialDelay = MIGRATION_RETRY_BASE_DELAY_MS * Math.pow(2, attempt - 1);
|
|
1071
|
+
const jitter = Math.random() * exponentialDelay * 0.5;
|
|
1072
|
+
const delay = Math.min(exponentialDelay + jitter, MIGRATION_RETRY_MAX_DELAY_MS);
|
|
1073
|
+
const buf = new SharedArrayBuffer(4);
|
|
1074
|
+
Atomics.wait(new Int32Array(buf), 0, 0, Math.round(delay));
|
|
978
1075
|
}
|
|
979
|
-
nativeDb.prepare("COMMIT").run();
|
|
980
|
-
} catch (err) {
|
|
981
|
-
nativeDb.prepare("ROLLBACK").run();
|
|
982
|
-
throw err;
|
|
983
1076
|
}
|
|
1077
|
+
throw lastError;
|
|
984
1078
|
}, { migrationsFolder });
|
|
985
1079
|
}
|
|
986
1080
|
function closeDb() {
|
|
@@ -1021,13 +1115,14 @@ function dbExists(cwd) {
|
|
|
1021
1115
|
function getNativeDb() {
|
|
1022
1116
|
return _nativeDb;
|
|
1023
1117
|
}
|
|
1024
|
-
var _require2, DatabaseSync2, DB_FILENAME, SQLITE_SCHEMA_VERSION, SCHEMA_VERSION, _db, _nativeDb, _dbPath, _initPromise;
|
|
1118
|
+
var _require2, DatabaseSync2, DB_FILENAME, SQLITE_SCHEMA_VERSION, SCHEMA_VERSION, _db, _nativeDb, _dbPath, _initPromise, _gitTrackingChecked, MAX_MIGRATION_RETRIES, MIGRATION_RETRY_BASE_DELAY_MS, MIGRATION_RETRY_MAX_DELAY_MS;
|
|
1025
1119
|
var init_sqlite = __esm({
|
|
1026
1120
|
"src/store/sqlite.ts"() {
|
|
1027
1121
|
"use strict";
|
|
1028
1122
|
init_schema();
|
|
1029
1123
|
init_paths();
|
|
1030
1124
|
init_node_sqlite_adapter();
|
|
1125
|
+
init_logger();
|
|
1031
1126
|
_require2 = createRequire2(import.meta.url);
|
|
1032
1127
|
({ DatabaseSync: DatabaseSync2 } = _require2("node:sqlite"));
|
|
1033
1128
|
DB_FILENAME = "tasks.db";
|
|
@@ -1037,6 +1132,10 @@ var init_sqlite = __esm({
|
|
|
1037
1132
|
_nativeDb = null;
|
|
1038
1133
|
_dbPath = null;
|
|
1039
1134
|
_initPromise = null;
|
|
1135
|
+
_gitTrackingChecked = false;
|
|
1136
|
+
MAX_MIGRATION_RETRIES = 5;
|
|
1137
|
+
MIGRATION_RETRY_BASE_DELAY_MS = 100;
|
|
1138
|
+
MIGRATION_RETRY_MAX_DELAY_MS = 2e3;
|
|
1040
1139
|
}
|
|
1041
1140
|
});
|
|
1042
1141
|
|
|
@@ -1803,6 +1902,30 @@ async function loadDependenciesForTasks(db, tasks2, validationIds) {
|
|
|
1803
1902
|
}
|
|
1804
1903
|
}
|
|
1805
1904
|
}
|
|
1905
|
+
async function loadRelationsForTasks(db, tasks2) {
|
|
1906
|
+
if (tasks2.length === 0) return;
|
|
1907
|
+
const taskIds = tasks2.map((t) => t.id);
|
|
1908
|
+
const allRels = await db.select().from(taskRelations).where(inArray(taskRelations.taskId, taskIds)).all();
|
|
1909
|
+
const relMap = /* @__PURE__ */ new Map();
|
|
1910
|
+
for (const rel of allRels) {
|
|
1911
|
+
let arr = relMap.get(rel.taskId);
|
|
1912
|
+
if (!arr) {
|
|
1913
|
+
arr = [];
|
|
1914
|
+
relMap.set(rel.taskId, arr);
|
|
1915
|
+
}
|
|
1916
|
+
arr.push({
|
|
1917
|
+
taskId: rel.relatedTo,
|
|
1918
|
+
type: rel.relationType,
|
|
1919
|
+
reason: rel.reason ?? void 0
|
|
1920
|
+
});
|
|
1921
|
+
}
|
|
1922
|
+
for (const task of tasks2) {
|
|
1923
|
+
const relations = relMap.get(task.id);
|
|
1924
|
+
if (relations && relations.length > 0) {
|
|
1925
|
+
task.relates = relations;
|
|
1926
|
+
}
|
|
1927
|
+
}
|
|
1928
|
+
}
|
|
1806
1929
|
var init_db_helpers = __esm({
|
|
1807
1930
|
"src/store/db-helpers.ts"() {
|
|
1808
1931
|
"use strict";
|
|
@@ -1851,6 +1974,7 @@ async function createSqliteDataAccessor(cwd) {
|
|
|
1851
1974
|
const tasks2 = taskRows.map(rowToTask);
|
|
1852
1975
|
if (tasks2.length > 0) {
|
|
1853
1976
|
await loadDependenciesForTasks(db, tasks2);
|
|
1977
|
+
await loadRelationsForTasks(db, tasks2);
|
|
1854
1978
|
}
|
|
1855
1979
|
const projectMeta = await getMetaValue(cwd, "project_meta") ?? DEFAULT_PROJECT_META;
|
|
1856
1980
|
const workState = await getMetaValue(cwd, "focus_state") ?? DEFAULT_WORK_STATE;
|
|
@@ -1926,6 +2050,7 @@ async function createSqliteDataAccessor(cwd) {
|
|
|
1926
2050
|
...activeRows.map((r) => r.id)
|
|
1927
2051
|
]);
|
|
1928
2052
|
await loadDependenciesForTasks(db, archivedTasks, allKnownIds);
|
|
2053
|
+
await loadRelationsForTasks(db, archivedTasks);
|
|
1929
2054
|
}
|
|
1930
2055
|
return {
|
|
1931
2056
|
archivedTasks,
|
|
@@ -1992,6 +2117,14 @@ async function createSqliteDataAccessor(cwd) {
|
|
|
1992
2117
|
await upsertTask(db, row);
|
|
1993
2118
|
await updateDependencies(db, task.id, task.depends ?? []);
|
|
1994
2119
|
},
|
|
2120
|
+
async addRelation(taskId, relatedTo, relationType, reason) {
|
|
2121
|
+
const db = await getDb(cwd);
|
|
2122
|
+
const validTypes = ["related", "blocks", "duplicates", "absorbs", "fixes", "extends", "supersedes"];
|
|
2123
|
+
if (!validTypes.includes(relationType)) {
|
|
2124
|
+
throw new Error(`Invalid relation type: ${relationType}. Valid types: ${validTypes.join(", ")}`);
|
|
2125
|
+
}
|
|
2126
|
+
await db.insert(taskRelations).values({ taskId, relatedTo, relationType, reason: reason ?? null }).onConflictDoNothing().run();
|
|
2127
|
+
},
|
|
1995
2128
|
async archiveSingleTask(taskId, fields) {
|
|
1996
2129
|
const db = await getDb(cwd);
|
|
1997
2130
|
const rows = await db.select({ id: tasks.id }).from(tasks).where(eq3(tasks.id, taskId)).all();
|
|
@@ -2066,9 +2199,9 @@ import { readFile as readFile2, writeFile } from "node:fs/promises";
|
|
|
2066
2199
|
import { existsSync as existsSync4 } from "node:fs";
|
|
2067
2200
|
import { execFile } from "node:child_process";
|
|
2068
2201
|
import { promisify } from "node:util";
|
|
2069
|
-
import { join as join5, resolve as
|
|
2202
|
+
import { join as join5, resolve as resolve4 } from "node:path";
|
|
2070
2203
|
function makeCleoGitEnv(cleoDir) {
|
|
2071
|
-
const abs =
|
|
2204
|
+
const abs = resolve4(cleoDir);
|
|
2072
2205
|
return {
|
|
2073
2206
|
...process.env,
|
|
2074
2207
|
GIT_DIR: join5(abs, ".git"),
|
|
@@ -2078,7 +2211,7 @@ function makeCleoGitEnv(cleoDir) {
|
|
|
2078
2211
|
async function cleoGitCommand(args, cleoDir) {
|
|
2079
2212
|
try {
|
|
2080
2213
|
const result = await execFileAsync("git", args, {
|
|
2081
|
-
cwd:
|
|
2214
|
+
cwd: resolve4(cleoDir),
|
|
2082
2215
|
// absolute cwd so relative paths in args resolve correctly
|
|
2083
2216
|
env: makeCleoGitEnv(cleoDir),
|
|
2084
2217
|
timeout: 1e4
|
|
@@ -2294,6 +2427,7 @@ var init_sqlite_backup = __esm({
|
|
|
2294
2427
|
// src/core/sequence/index.ts
|
|
2295
2428
|
var sequence_exports = {};
|
|
2296
2429
|
__export(sequence_exports, {
|
|
2430
|
+
allocateNextTaskId: () => allocateNextTaskId,
|
|
2297
2431
|
checkSequence: () => checkSequence,
|
|
2298
2432
|
repairSequence: () => repairSequence,
|
|
2299
2433
|
showSequence: () => showSequence
|
|
@@ -2480,7 +2614,56 @@ async function repairSequence(cwd, accessor) {
|
|
|
2480
2614
|
message: `Sequence repaired: ${oldCounter} -> ${newCounter}`
|
|
2481
2615
|
};
|
|
2482
2616
|
}
|
|
2483
|
-
|
|
2617
|
+
async function allocateNextTaskId(cwd, retryCount = 0) {
|
|
2618
|
+
await getDb(cwd);
|
|
2619
|
+
const nativeDb = getNativeDb();
|
|
2620
|
+
if (!nativeDb) {
|
|
2621
|
+
throw new CleoError(3 /* FILE_ERROR */, "Native database not available for atomic ID allocation");
|
|
2622
|
+
}
|
|
2623
|
+
nativeDb.prepare("BEGIN IMMEDIATE").run();
|
|
2624
|
+
try {
|
|
2625
|
+
nativeDb.prepare(`
|
|
2626
|
+
UPDATE schema_meta
|
|
2627
|
+
SET value = json_set(value,
|
|
2628
|
+
'$.counter', json_extract(value, '$.counter') + 1,
|
|
2629
|
+
'$.lastId', 'T' || printf('%03d', json_extract(value, '$.counter') + 1),
|
|
2630
|
+
'$.checksum', 'alloc-' || strftime('%s','now')
|
|
2631
|
+
)
|
|
2632
|
+
WHERE key = 'task_id_sequence'
|
|
2633
|
+
`).run();
|
|
2634
|
+
const row = nativeDb.prepare(`
|
|
2635
|
+
SELECT json_extract(value, '$.counter') AS counter
|
|
2636
|
+
FROM schema_meta WHERE key = 'task_id_sequence'
|
|
2637
|
+
`).get();
|
|
2638
|
+
if (!row) {
|
|
2639
|
+
throw new CleoError(3 /* FILE_ERROR */, "Sequence counter not found after increment");
|
|
2640
|
+
}
|
|
2641
|
+
const newId = `T${String(row.counter).padStart(3, "0")}`;
|
|
2642
|
+
const existing = nativeDb.prepare(
|
|
2643
|
+
"SELECT id FROM tasks WHERE id = ?"
|
|
2644
|
+
).get(newId);
|
|
2645
|
+
if (existing) {
|
|
2646
|
+
nativeDb.prepare("ROLLBACK").run();
|
|
2647
|
+
if (retryCount >= MAX_ALLOC_RETRIES) {
|
|
2648
|
+
throw new CleoError(
|
|
2649
|
+
22 /* ID_COLLISION */,
|
|
2650
|
+
`Failed to allocate unique task ID after ${MAX_ALLOC_RETRIES} retries (last attempted: ${newId})`
|
|
2651
|
+
);
|
|
2652
|
+
}
|
|
2653
|
+
await repairSequence(cwd);
|
|
2654
|
+
return allocateNextTaskId(cwd, retryCount + 1);
|
|
2655
|
+
}
|
|
2656
|
+
nativeDb.prepare("COMMIT").run();
|
|
2657
|
+
return newId;
|
|
2658
|
+
} catch (err) {
|
|
2659
|
+
try {
|
|
2660
|
+
nativeDb.prepare("ROLLBACK").run();
|
|
2661
|
+
} catch {
|
|
2662
|
+
}
|
|
2663
|
+
throw err;
|
|
2664
|
+
}
|
|
2665
|
+
}
|
|
2666
|
+
var SEQUENCE_META_KEY, MAX_ALLOC_RETRIES;
|
|
2484
2667
|
var init_sequence = __esm({
|
|
2485
2668
|
"src/core/sequence/index.ts"() {
|
|
2486
2669
|
"use strict";
|
|
@@ -2491,6 +2674,7 @@ var init_sequence = __esm({
|
|
|
2491
2674
|
init_sqlite_data_accessor();
|
|
2492
2675
|
init_data_accessor();
|
|
2493
2676
|
SEQUENCE_META_KEY = "task_id_sequence";
|
|
2677
|
+
MAX_ALLOC_RETRIES = 3;
|
|
2494
2678
|
}
|
|
2495
2679
|
});
|
|
2496
2680
|
|
|
@@ -2803,6 +2987,11 @@ var init_safety_data_accessor = __esm({
|
|
|
2803
2987
|
this.getSafetyOptions()
|
|
2804
2988
|
);
|
|
2805
2989
|
}
|
|
2990
|
+
// ---- Relations (pass-through to inner, T5168) ----
|
|
2991
|
+
async addRelation(taskId, relatedTo, relationType, reason) {
|
|
2992
|
+
if (!this.inner.addRelation) return;
|
|
2993
|
+
await this.inner.addRelation(taskId, relatedTo, relationType, reason);
|
|
2994
|
+
}
|
|
2806
2995
|
// ---- Metadata (pass-through to inner) ----
|
|
2807
2996
|
async getMetaValue(key) {
|
|
2808
2997
|
return this.inner.getMetaValue?.(key) ?? null;
|
|
@@ -2838,10 +3027,57 @@ var init_data_accessor = __esm({
|
|
|
2838
3027
|
}
|
|
2839
3028
|
});
|
|
2840
3029
|
|
|
2841
|
-
// src/
|
|
2842
|
-
import { readFileSync as readFileSync4, writeFileSync, renameSync as renameSync3,
|
|
2843
|
-
import { join as join8, dirname as dirname5, basename as basename2 } from "path";
|
|
3030
|
+
// src/store/file-utils.ts
|
|
3031
|
+
import { readFileSync as readFileSync4, writeFileSync, renameSync as renameSync3, existsSync as existsSync8, unlinkSync as unlinkSync2, mkdirSync as mkdirSync3, readdirSync as readdirSync2 } from "node:fs";
|
|
3032
|
+
import { join as join8, dirname as dirname5, basename as basename2 } from "node:path";
|
|
3033
|
+
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
2844
3034
|
import * as lockfile2 from "proper-lockfile";
|
|
3035
|
+
function rotateBackup(filePath) {
|
|
3036
|
+
const dir = dirname5(filePath);
|
|
3037
|
+
const name = basename2(filePath);
|
|
3038
|
+
const backupDir = join8(dir, ".backups");
|
|
3039
|
+
if (!existsSync8(backupDir)) {
|
|
3040
|
+
mkdirSync3(backupDir, { recursive: true });
|
|
3041
|
+
}
|
|
3042
|
+
for (let i = MAX_BACKUPS; i >= 1; i--) {
|
|
3043
|
+
const current = join8(backupDir, `${name}.${i}`);
|
|
3044
|
+
if (i === MAX_BACKUPS) {
|
|
3045
|
+
try {
|
|
3046
|
+
unlinkSync2(current);
|
|
3047
|
+
} catch {
|
|
3048
|
+
}
|
|
3049
|
+
} else {
|
|
3050
|
+
const next = join8(backupDir, `${name}.${i + 1}`);
|
|
3051
|
+
try {
|
|
3052
|
+
if (existsSync8(current)) renameSync3(current, next);
|
|
3053
|
+
} catch {
|
|
3054
|
+
}
|
|
3055
|
+
}
|
|
3056
|
+
}
|
|
3057
|
+
try {
|
|
3058
|
+
const content = readFileSync4(filePath, "utf-8");
|
|
3059
|
+
writeFileSync(join8(backupDir, `${name}.1`), content, "utf-8");
|
|
3060
|
+
} catch {
|
|
3061
|
+
}
|
|
3062
|
+
}
|
|
3063
|
+
function writeJsonFileAtomic(filePath, data, indent = 2) {
|
|
3064
|
+
const dir = dirname5(filePath);
|
|
3065
|
+
const tempPath = join8(dir, `.${basename2(filePath)}.${randomBytes2(6).toString("hex")}.tmp`);
|
|
3066
|
+
const content = JSON.stringify(data, null, indent) + "\n";
|
|
3067
|
+
writeFileSync(tempPath, content, "utf-8");
|
|
3068
|
+
try {
|
|
3069
|
+
if (existsSync8(filePath)) {
|
|
3070
|
+
rotateBackup(filePath);
|
|
3071
|
+
}
|
|
3072
|
+
renameSync3(tempPath, filePath);
|
|
3073
|
+
} catch (error) {
|
|
3074
|
+
try {
|
|
3075
|
+
unlinkSync2(tempPath);
|
|
3076
|
+
} catch {
|
|
3077
|
+
}
|
|
3078
|
+
throw error;
|
|
3079
|
+
}
|
|
3080
|
+
}
|
|
2845
3081
|
function readJsonFile(filePath) {
|
|
2846
3082
|
try {
|
|
2847
3083
|
const content = readFileSync4(filePath, "utf-8");
|
|
@@ -2853,12 +3089,95 @@ function readJsonFile(filePath) {
|
|
|
2853
3089
|
throw error;
|
|
2854
3090
|
}
|
|
2855
3091
|
}
|
|
3092
|
+
function readLogFileEntries(filePath) {
|
|
3093
|
+
let content;
|
|
3094
|
+
try {
|
|
3095
|
+
content = readFileSync4(filePath, "utf-8").trim();
|
|
3096
|
+
} catch (error) {
|
|
3097
|
+
if (error.code === "ENOENT") return [];
|
|
3098
|
+
throw error;
|
|
3099
|
+
}
|
|
3100
|
+
if (!content) return [];
|
|
3101
|
+
try {
|
|
3102
|
+
const parsed = JSON.parse(content);
|
|
3103
|
+
if (Array.isArray(parsed)) return parsed;
|
|
3104
|
+
if (parsed && typeof parsed === "object" && Array.isArray(parsed.entries)) {
|
|
3105
|
+
return parsed.entries;
|
|
3106
|
+
}
|
|
3107
|
+
return [parsed];
|
|
3108
|
+
} catch {
|
|
3109
|
+
}
|
|
3110
|
+
const entries = [];
|
|
3111
|
+
if (content.startsWith("{")) {
|
|
3112
|
+
let depth = 0;
|
|
3113
|
+
let inString = false;
|
|
3114
|
+
let escaped = false;
|
|
3115
|
+
let jsonEnd = -1;
|
|
3116
|
+
for (let i = 0; i < content.length; i++) {
|
|
3117
|
+
const ch = content[i];
|
|
3118
|
+
if (escaped) {
|
|
3119
|
+
escaped = false;
|
|
3120
|
+
continue;
|
|
3121
|
+
}
|
|
3122
|
+
if (ch === "\\" && inString) {
|
|
3123
|
+
escaped = true;
|
|
3124
|
+
continue;
|
|
3125
|
+
}
|
|
3126
|
+
if (ch === '"') {
|
|
3127
|
+
inString = !inString;
|
|
3128
|
+
continue;
|
|
3129
|
+
}
|
|
3130
|
+
if (inString) continue;
|
|
3131
|
+
if (ch === "{") depth++;
|
|
3132
|
+
else if (ch === "}") {
|
|
3133
|
+
depth--;
|
|
3134
|
+
if (depth === 0) {
|
|
3135
|
+
jsonEnd = i + 1;
|
|
3136
|
+
break;
|
|
3137
|
+
}
|
|
3138
|
+
}
|
|
3139
|
+
}
|
|
3140
|
+
if (jsonEnd > 0) {
|
|
3141
|
+
try {
|
|
3142
|
+
const initial = JSON.parse(content.substring(0, jsonEnd));
|
|
3143
|
+
if (initial && Array.isArray(initial.entries)) entries.push(...initial.entries);
|
|
3144
|
+
} catch {
|
|
3145
|
+
}
|
|
3146
|
+
const remainder = content.substring(jsonEnd).trim();
|
|
3147
|
+
if (remainder) {
|
|
3148
|
+
for (const line of remainder.split("\n")) {
|
|
3149
|
+
const l = line.trim();
|
|
3150
|
+
if (!l || !l.startsWith("{")) continue;
|
|
3151
|
+
try {
|
|
3152
|
+
entries.push(JSON.parse(l));
|
|
3153
|
+
} catch {
|
|
3154
|
+
}
|
|
3155
|
+
}
|
|
3156
|
+
}
|
|
3157
|
+
}
|
|
3158
|
+
} else {
|
|
3159
|
+
for (const line of content.split("\n")) {
|
|
3160
|
+
const l = line.trim();
|
|
3161
|
+
if (!l || !l.startsWith("{")) continue;
|
|
3162
|
+
try {
|
|
3163
|
+
entries.push(JSON.parse(l));
|
|
3164
|
+
} catch {
|
|
3165
|
+
}
|
|
3166
|
+
}
|
|
3167
|
+
}
|
|
3168
|
+
return entries;
|
|
3169
|
+
}
|
|
2856
3170
|
function getDataPath(projectRoot, filename) {
|
|
2857
3171
|
return join8(projectRoot, ".cleo", filename);
|
|
2858
3172
|
}
|
|
2859
|
-
|
|
2860
|
-
"
|
|
3173
|
+
function resolveProjectRoot() {
|
|
3174
|
+
return process.env["CLEO_ROOT"] || process.cwd();
|
|
3175
|
+
}
|
|
3176
|
+
var MAX_BACKUPS;
|
|
3177
|
+
var init_file_utils = __esm({
|
|
3178
|
+
"src/store/file-utils.ts"() {
|
|
2861
3179
|
"use strict";
|
|
3180
|
+
MAX_BACKUPS = 10;
|
|
2862
3181
|
}
|
|
2863
3182
|
});
|
|
2864
3183
|
|
|
@@ -3102,7 +3421,7 @@ var init_plan = __esm({
|
|
|
3102
3421
|
"src/core/tasks/plan.ts"() {
|
|
3103
3422
|
"use strict";
|
|
3104
3423
|
init_data_accessor();
|
|
3105
|
-
|
|
3424
|
+
init_file_utils();
|
|
3106
3425
|
init_deps_ready();
|
|
3107
3426
|
PRIORITY_SCORE2 = {
|
|
3108
3427
|
critical: 100,
|
|
@@ -3114,7 +3433,7 @@ var init_plan = __esm({
|
|
|
3114
3433
|
});
|
|
3115
3434
|
|
|
3116
3435
|
// src/core/sessions/decisions.ts
|
|
3117
|
-
import { randomBytes as
|
|
3436
|
+
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
3118
3437
|
import { readFileSync as readFileSync5, appendFileSync, mkdirSync as mkdirSync4, existsSync as existsSync9 } from "node:fs";
|
|
3119
3438
|
import { join as join9 } from "node:path";
|
|
3120
3439
|
async function recordDecision(projectRoot, params) {
|
|
@@ -3130,7 +3449,7 @@ async function recordDecision(projectRoot, params) {
|
|
|
3130
3449
|
}
|
|
3131
3450
|
const decisionPath = join9(auditDir, "decisions.jsonl");
|
|
3132
3451
|
const record = {
|
|
3133
|
-
id: `dec-${
|
|
3452
|
+
id: `dec-${randomBytes3(8).toString("hex")}`,
|
|
3134
3453
|
sessionId: params.sessionId,
|
|
3135
3454
|
taskId: params.taskId,
|
|
3136
3455
|
decision: params.decision,
|
|
@@ -3416,189 +3735,36 @@ async function computeChainPosition(projectRoot, sessionId) {
|
|
|
3416
3735
|
let current = sessionId;
|
|
3417
3736
|
let position = 1;
|
|
3418
3737
|
const visited = /* @__PURE__ */ new Set();
|
|
3419
|
-
while (true) {
|
|
3420
|
-
visited.add(current);
|
|
3421
|
-
const session = sessionMap.get(current);
|
|
3422
|
-
if (!session?.previousSessionId || visited.has(session.previousSessionId)) break;
|
|
3423
|
-
current = session.previousSessionId;
|
|
3424
|
-
position++;
|
|
3425
|
-
}
|
|
3426
|
-
let length = position;
|
|
3427
|
-
const startSession = sessionMap.get(sessionId);
|
|
3428
|
-
let fwd = startSession?.nextSessionId;
|
|
3429
|
-
while (fwd && !visited.has(fwd)) {
|
|
3430
|
-
visited.add(fwd);
|
|
3431
|
-
length++;
|
|
3432
|
-
const s = sessionMap.get(fwd);
|
|
3433
|
-
fwd = s?.nextSessionId ?? void 0;
|
|
3434
|
-
}
|
|
3435
|
-
return { position, length };
|
|
3436
|
-
} catch {
|
|
3437
|
-
return { position: 1, length: 1 };
|
|
3438
|
-
}
|
|
3439
|
-
}
|
|
3440
|
-
var execFileAsync2;
|
|
3441
|
-
var init_handoff = __esm({
|
|
3442
|
-
"src/core/sessions/handoff.ts"() {
|
|
3443
|
-
"use strict";
|
|
3444
|
-
init_data_accessor();
|
|
3445
|
-
init_errors();
|
|
3446
|
-
init_exit_codes();
|
|
3447
|
-
init_decisions();
|
|
3448
|
-
execFileAsync2 = promisify2(execFile2);
|
|
3449
|
-
}
|
|
3450
|
-
});
|
|
3451
|
-
|
|
3452
|
-
// src/store/file-utils.ts
|
|
3453
|
-
import { readFileSync as readFileSync6, writeFileSync as writeFileSync2, renameSync as renameSync4, existsSync as existsSync11, unlinkSync as unlinkSync3, mkdirSync as mkdirSync6 } from "node:fs";
|
|
3454
|
-
import { join as join11, dirname as dirname6, basename as basename3 } from "node:path";
|
|
3455
|
-
import { randomBytes as randomBytes5 } from "node:crypto";
|
|
3456
|
-
function rotateBackup(filePath) {
|
|
3457
|
-
const dir = dirname6(filePath);
|
|
3458
|
-
const name = basename3(filePath);
|
|
3459
|
-
const backupDir = join11(dir, ".backups");
|
|
3460
|
-
if (!existsSync11(backupDir)) {
|
|
3461
|
-
mkdirSync6(backupDir, { recursive: true });
|
|
3462
|
-
}
|
|
3463
|
-
for (let i = MAX_BACKUPS; i >= 1; i--) {
|
|
3464
|
-
const current = join11(backupDir, `${name}.${i}`);
|
|
3465
|
-
if (i === MAX_BACKUPS) {
|
|
3466
|
-
try {
|
|
3467
|
-
unlinkSync3(current);
|
|
3468
|
-
} catch {
|
|
3469
|
-
}
|
|
3470
|
-
} else {
|
|
3471
|
-
const next = join11(backupDir, `${name}.${i + 1}`);
|
|
3472
|
-
try {
|
|
3473
|
-
if (existsSync11(current)) renameSync4(current, next);
|
|
3474
|
-
} catch {
|
|
3475
|
-
}
|
|
3476
|
-
}
|
|
3477
|
-
}
|
|
3478
|
-
try {
|
|
3479
|
-
const content = readFileSync6(filePath, "utf-8");
|
|
3480
|
-
writeFileSync2(join11(backupDir, `${name}.1`), content, "utf-8");
|
|
3481
|
-
} catch {
|
|
3482
|
-
}
|
|
3483
|
-
}
|
|
3484
|
-
function writeJsonFileAtomic(filePath, data, indent = 2) {
|
|
3485
|
-
const dir = dirname6(filePath);
|
|
3486
|
-
const tempPath = join11(dir, `.${basename3(filePath)}.${randomBytes5(6).toString("hex")}.tmp`);
|
|
3487
|
-
const content = JSON.stringify(data, null, indent) + "\n";
|
|
3488
|
-
writeFileSync2(tempPath, content, "utf-8");
|
|
3489
|
-
try {
|
|
3490
|
-
if (existsSync11(filePath)) {
|
|
3491
|
-
rotateBackup(filePath);
|
|
3492
|
-
}
|
|
3493
|
-
renameSync4(tempPath, filePath);
|
|
3494
|
-
} catch (error) {
|
|
3495
|
-
try {
|
|
3496
|
-
unlinkSync3(tempPath);
|
|
3497
|
-
} catch {
|
|
3498
|
-
}
|
|
3499
|
-
throw error;
|
|
3500
|
-
}
|
|
3501
|
-
}
|
|
3502
|
-
function readJsonFile2(filePath) {
|
|
3503
|
-
try {
|
|
3504
|
-
const content = readFileSync6(filePath, "utf-8");
|
|
3505
|
-
return JSON.parse(content);
|
|
3506
|
-
} catch (error) {
|
|
3507
|
-
if (error.code === "ENOENT") {
|
|
3508
|
-
return null;
|
|
3509
|
-
}
|
|
3510
|
-
throw error;
|
|
3511
|
-
}
|
|
3512
|
-
}
|
|
3513
|
-
function readLogFileEntries2(filePath) {
|
|
3514
|
-
let content;
|
|
3515
|
-
try {
|
|
3516
|
-
content = readFileSync6(filePath, "utf-8").trim();
|
|
3517
|
-
} catch (error) {
|
|
3518
|
-
if (error.code === "ENOENT") return [];
|
|
3519
|
-
throw error;
|
|
3520
|
-
}
|
|
3521
|
-
if (!content) return [];
|
|
3522
|
-
try {
|
|
3523
|
-
const parsed = JSON.parse(content);
|
|
3524
|
-
if (Array.isArray(parsed)) return parsed;
|
|
3525
|
-
if (parsed && typeof parsed === "object" && Array.isArray(parsed.entries)) {
|
|
3526
|
-
return parsed.entries;
|
|
3527
|
-
}
|
|
3528
|
-
return [parsed];
|
|
3529
|
-
} catch {
|
|
3530
|
-
}
|
|
3531
|
-
const entries = [];
|
|
3532
|
-
if (content.startsWith("{")) {
|
|
3533
|
-
let depth = 0;
|
|
3534
|
-
let inString = false;
|
|
3535
|
-
let escaped = false;
|
|
3536
|
-
let jsonEnd = -1;
|
|
3537
|
-
for (let i = 0; i < content.length; i++) {
|
|
3538
|
-
const ch = content[i];
|
|
3539
|
-
if (escaped) {
|
|
3540
|
-
escaped = false;
|
|
3541
|
-
continue;
|
|
3542
|
-
}
|
|
3543
|
-
if (ch === "\\" && inString) {
|
|
3544
|
-
escaped = true;
|
|
3545
|
-
continue;
|
|
3546
|
-
}
|
|
3547
|
-
if (ch === '"') {
|
|
3548
|
-
inString = !inString;
|
|
3549
|
-
continue;
|
|
3550
|
-
}
|
|
3551
|
-
if (inString) continue;
|
|
3552
|
-
if (ch === "{") depth++;
|
|
3553
|
-
else if (ch === "}") {
|
|
3554
|
-
depth--;
|
|
3555
|
-
if (depth === 0) {
|
|
3556
|
-
jsonEnd = i + 1;
|
|
3557
|
-
break;
|
|
3558
|
-
}
|
|
3559
|
-
}
|
|
3560
|
-
}
|
|
3561
|
-
if (jsonEnd > 0) {
|
|
3562
|
-
try {
|
|
3563
|
-
const initial = JSON.parse(content.substring(0, jsonEnd));
|
|
3564
|
-
if (initial && Array.isArray(initial.entries)) entries.push(...initial.entries);
|
|
3565
|
-
} catch {
|
|
3566
|
-
}
|
|
3567
|
-
const remainder = content.substring(jsonEnd).trim();
|
|
3568
|
-
if (remainder) {
|
|
3569
|
-
for (const line of remainder.split("\n")) {
|
|
3570
|
-
const l = line.trim();
|
|
3571
|
-
if (!l || !l.startsWith("{")) continue;
|
|
3572
|
-
try {
|
|
3573
|
-
entries.push(JSON.parse(l));
|
|
3574
|
-
} catch {
|
|
3575
|
-
}
|
|
3576
|
-
}
|
|
3577
|
-
}
|
|
3578
|
-
}
|
|
3579
|
-
} else {
|
|
3580
|
-
for (const line of content.split("\n")) {
|
|
3581
|
-
const l = line.trim();
|
|
3582
|
-
if (!l || !l.startsWith("{")) continue;
|
|
3583
|
-
try {
|
|
3584
|
-
entries.push(JSON.parse(l));
|
|
3585
|
-
} catch {
|
|
3586
|
-
}
|
|
3738
|
+
while (true) {
|
|
3739
|
+
visited.add(current);
|
|
3740
|
+
const session = sessionMap.get(current);
|
|
3741
|
+
if (!session?.previousSessionId || visited.has(session.previousSessionId)) break;
|
|
3742
|
+
current = session.previousSessionId;
|
|
3743
|
+
position++;
|
|
3744
|
+
}
|
|
3745
|
+
let length = position;
|
|
3746
|
+
const startSession = sessionMap.get(sessionId);
|
|
3747
|
+
let fwd = startSession?.nextSessionId;
|
|
3748
|
+
while (fwd && !visited.has(fwd)) {
|
|
3749
|
+
visited.add(fwd);
|
|
3750
|
+
length++;
|
|
3751
|
+
const s = sessionMap.get(fwd);
|
|
3752
|
+
fwd = s?.nextSessionId ?? void 0;
|
|
3587
3753
|
}
|
|
3754
|
+
return { position, length };
|
|
3755
|
+
} catch {
|
|
3756
|
+
return { position: 1, length: 1 };
|
|
3588
3757
|
}
|
|
3589
|
-
return entries;
|
|
3590
|
-
}
|
|
3591
|
-
function getDataPath2(projectRoot, filename) {
|
|
3592
|
-
return join11(projectRoot, ".cleo", filename);
|
|
3593
3758
|
}
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
var MAX_BACKUPS;
|
|
3598
|
-
var init_file_utils = __esm({
|
|
3599
|
-
"src/store/file-utils.ts"() {
|
|
3759
|
+
var execFileAsync2;
|
|
3760
|
+
var init_handoff = __esm({
|
|
3761
|
+
"src/core/sessions/handoff.ts"() {
|
|
3600
3762
|
"use strict";
|
|
3601
|
-
|
|
3763
|
+
init_data_accessor();
|
|
3764
|
+
init_errors();
|
|
3765
|
+
init_exit_codes();
|
|
3766
|
+
init_decisions();
|
|
3767
|
+
execFileAsync2 = promisify2(execFile2);
|
|
3602
3768
|
}
|
|
3603
3769
|
});
|
|
3604
3770
|
|
|
@@ -3613,15 +3779,15 @@ __export(platform_exports, {
|
|
|
3613
3779
|
dateDaysAgo: () => dateDaysAgo,
|
|
3614
3780
|
detectPlatform: () => detectPlatform,
|
|
3615
3781
|
generateRandomHex: () => generateRandomHex,
|
|
3616
|
-
getDataPath: () =>
|
|
3782
|
+
getDataPath: () => getDataPath,
|
|
3617
3783
|
getFileMtime: () => getFileMtime,
|
|
3618
3784
|
getFileSize: () => getFileSize,
|
|
3619
3785
|
getIsoTimestamp: () => getIsoTimestamp,
|
|
3620
3786
|
getNodeUpgradeInstructions: () => getNodeUpgradeInstructions,
|
|
3621
3787
|
getNodeVersionInfo: () => getNodeVersionInfo,
|
|
3622
3788
|
isoToEpoch: () => isoToEpoch,
|
|
3623
|
-
readJsonFile: () =>
|
|
3624
|
-
readLogFileEntries: () =>
|
|
3789
|
+
readJsonFile: () => readJsonFile,
|
|
3790
|
+
readLogFileEntries: () => readLogFileEntries,
|
|
3625
3791
|
requireTool: () => requireTool,
|
|
3626
3792
|
resolveProjectRoot: () => resolveProjectRoot,
|
|
3627
3793
|
sha256: () => sha256,
|
|
@@ -3629,8 +3795,8 @@ __export(platform_exports, {
|
|
|
3629
3795
|
});
|
|
3630
3796
|
import { execFileSync } from "node:child_process";
|
|
3631
3797
|
import { tmpdir } from "node:os";
|
|
3632
|
-
import { join as
|
|
3633
|
-
import { existsSync as
|
|
3798
|
+
import { join as join11 } from "node:path";
|
|
3799
|
+
import { existsSync as existsSync11, statSync as statSync2 } from "node:fs";
|
|
3634
3800
|
import { createHash as createHash3, randomBytes as randomBytes6 } from "node:crypto";
|
|
3635
3801
|
function detectPlatform() {
|
|
3636
3802
|
switch (process.platform) {
|
|
@@ -3689,11 +3855,11 @@ function dateDaysAgo(days) {
|
|
|
3689
3855
|
return d.toISOString();
|
|
3690
3856
|
}
|
|
3691
3857
|
function getFileSize(filePath) {
|
|
3692
|
-
if (!
|
|
3858
|
+
if (!existsSync11(filePath)) return 0;
|
|
3693
3859
|
return statSync2(filePath).size;
|
|
3694
3860
|
}
|
|
3695
3861
|
function getFileMtime(filePath) {
|
|
3696
|
-
if (!
|
|
3862
|
+
if (!existsSync11(filePath)) return null;
|
|
3697
3863
|
return statSync2(filePath).mtime.toISOString();
|
|
3698
3864
|
}
|
|
3699
3865
|
function generateRandomHex(bytes = 6) {
|
|
@@ -3704,7 +3870,7 @@ function sha256(data) {
|
|
|
3704
3870
|
}
|
|
3705
3871
|
function createTempFilePath(prefix = "cleo-", suffix = ".tmp") {
|
|
3706
3872
|
const random = generateRandomHex(8);
|
|
3707
|
-
return
|
|
3873
|
+
return join11(tmpdir(), `${prefix}${random}${suffix}`);
|
|
3708
3874
|
}
|
|
3709
3875
|
function getNodeVersionInfo() {
|
|
3710
3876
|
const version = process.version.replace("v", "");
|
|
@@ -3834,20 +4000,20 @@ __export(schema_integrity_exports, {
|
|
|
3834
4000
|
checkSchemaIntegrity: () => checkSchemaIntegrity,
|
|
3835
4001
|
readSchemaVersionFromFile: () => readSchemaVersionFromFile
|
|
3836
4002
|
});
|
|
3837
|
-
import { existsSync as
|
|
3838
|
-
import { join as
|
|
4003
|
+
import { existsSync as existsSync21, readFileSync as readFileSync14 } from "node:fs";
|
|
4004
|
+
import { join as join21, dirname as dirname6 } from "node:path";
|
|
3839
4005
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
3840
4006
|
function resolveSchemaPath(schemaName) {
|
|
3841
|
-
const __dirname2 =
|
|
4007
|
+
const __dirname2 = dirname6(fileURLToPath2(import.meta.url));
|
|
3842
4008
|
const candidates = [
|
|
3843
4009
|
// dist/core/validation/ → schemas/
|
|
3844
|
-
|
|
4010
|
+
join21(__dirname2, "..", "..", "..", "schemas", schemaName),
|
|
3845
4011
|
// src/core/validation/ → schemas/ (ts-node / vitest)
|
|
3846
|
-
|
|
4012
|
+
join21(__dirname2, "..", "..", "schemas", schemaName),
|
|
3847
4013
|
// fallback: look relative to cwd
|
|
3848
|
-
|
|
4014
|
+
join21(process.cwd(), "schemas", schemaName)
|
|
3849
4015
|
];
|
|
3850
|
-
return candidates.find(
|
|
4016
|
+
return candidates.find(existsSync21) ?? null;
|
|
3851
4017
|
}
|
|
3852
4018
|
function readSchemaVersionFromFile(schemaName) {
|
|
3853
4019
|
const path = resolveSchemaPath(schemaName);
|
|
@@ -3856,7 +4022,7 @@ function readSchemaVersionFromFile(schemaName) {
|
|
|
3856
4022
|
}
|
|
3857
4023
|
function readSchemaVersion(schemaPath) {
|
|
3858
4024
|
try {
|
|
3859
|
-
const raw =
|
|
4025
|
+
const raw = readFileSync14(schemaPath, "utf-8");
|
|
3860
4026
|
const schema = JSON.parse(raw);
|
|
3861
4027
|
const version = schema["schemaVersion"] ?? schema["_meta"]?.["schemaVersion"];
|
|
3862
4028
|
return typeof version === "string" ? version : null;
|
|
@@ -3866,7 +4032,7 @@ function readSchemaVersion(schemaPath) {
|
|
|
3866
4032
|
}
|
|
3867
4033
|
function checkFile(target, cleoDir) {
|
|
3868
4034
|
const filePath = target.filePath(cleoDir);
|
|
3869
|
-
if (!
|
|
4035
|
+
if (!existsSync21(filePath)) {
|
|
3870
4036
|
if (target.required) {
|
|
3871
4037
|
return {
|
|
3872
4038
|
label: target.label,
|
|
@@ -3878,7 +4044,7 @@ function checkFile(target, cleoDir) {
|
|
|
3878
4044
|
}
|
|
3879
4045
|
let data;
|
|
3880
4046
|
try {
|
|
3881
|
-
data = JSON.parse(
|
|
4047
|
+
data = JSON.parse(readFileSync14(filePath, "utf-8"));
|
|
3882
4048
|
} catch (err) {
|
|
3883
4049
|
return {
|
|
3884
4050
|
label: target.label,
|
|
@@ -3896,7 +4062,7 @@ function checkFile(target, cleoDir) {
|
|
|
3896
4062
|
}
|
|
3897
4063
|
let schema;
|
|
3898
4064
|
try {
|
|
3899
|
-
schema = JSON.parse(
|
|
4065
|
+
schema = JSON.parse(readFileSync14(schemaPath, "utf-8"));
|
|
3900
4066
|
} catch (err) {
|
|
3901
4067
|
return {
|
|
3902
4068
|
label: target.label,
|
|
@@ -3960,28 +4126,28 @@ var init_schema_integrity = __esm({
|
|
|
3960
4126
|
INTEGRITY_TARGETS = [
|
|
3961
4127
|
{
|
|
3962
4128
|
label: "config.json",
|
|
3963
|
-
filePath: (d) =>
|
|
4129
|
+
filePath: (d) => join21(d, "config.json"),
|
|
3964
4130
|
schemaName: "config.schema.json",
|
|
3965
4131
|
required: true,
|
|
3966
4132
|
versionKey: "schemaVersion"
|
|
3967
4133
|
},
|
|
3968
4134
|
{
|
|
3969
4135
|
label: "project-info.json",
|
|
3970
|
-
filePath: (d) =>
|
|
4136
|
+
filePath: (d) => join21(d, "project-info.json"),
|
|
3971
4137
|
schemaName: "project-info.schema.json",
|
|
3972
4138
|
required: false,
|
|
3973
4139
|
versionKey: "schemaVersion"
|
|
3974
4140
|
},
|
|
3975
4141
|
{
|
|
3976
4142
|
label: "project-context.json",
|
|
3977
|
-
filePath: (d) =>
|
|
4143
|
+
filePath: (d) => join21(d, "project-context.json"),
|
|
3978
4144
|
schemaName: "project-context.schema.json",
|
|
3979
4145
|
required: false,
|
|
3980
4146
|
versionKey: "schemaVersion"
|
|
3981
4147
|
},
|
|
3982
4148
|
{
|
|
3983
4149
|
label: ".context-state.json",
|
|
3984
|
-
filePath: (d) =>
|
|
4150
|
+
filePath: (d) => join21(d, ".context-state.json"),
|
|
3985
4151
|
schemaName: "context-state.schema.json",
|
|
3986
4152
|
required: false,
|
|
3987
4153
|
versionKey: "version"
|
|
@@ -3996,17 +4162,17 @@ __export(mcp_exports, {
|
|
|
3996
4162
|
detectEnvMode: () => detectEnvMode,
|
|
3997
4163
|
generateMcpServerEntry: () => generateMcpServerEntry
|
|
3998
4164
|
});
|
|
3999
|
-
import { readFileSync as
|
|
4000
|
-
import { join as
|
|
4165
|
+
import { readFileSync as readFileSync15 } from "node:fs";
|
|
4166
|
+
import { join as join22 } from "node:path";
|
|
4001
4167
|
import { homedir as homedir3 } from "node:os";
|
|
4002
4168
|
function detectEnvMode() {
|
|
4003
|
-
const versionPath =
|
|
4004
|
-
process.env["CLEO_HOME"] ??
|
|
4169
|
+
const versionPath = join22(
|
|
4170
|
+
process.env["CLEO_HOME"] ?? join22(homedir3(), ".cleo"),
|
|
4005
4171
|
"VERSION"
|
|
4006
4172
|
);
|
|
4007
4173
|
let content;
|
|
4008
4174
|
try {
|
|
4009
|
-
content =
|
|
4175
|
+
content = readFileSync15(versionPath, "utf-8");
|
|
4010
4176
|
} catch {
|
|
4011
4177
|
return { mode: "unknown", source: null };
|
|
4012
4178
|
}
|
|
@@ -4029,7 +4195,7 @@ function generateMcpServerEntry(env) {
|
|
|
4029
4195
|
if (env.mode === "dev-ts" && env.source) {
|
|
4030
4196
|
return {
|
|
4031
4197
|
command: "node",
|
|
4032
|
-
args: [
|
|
4198
|
+
args: [join22(env.source, "dist", "mcp", "index.js")],
|
|
4033
4199
|
env: {}
|
|
4034
4200
|
};
|
|
4035
4201
|
}
|
|
@@ -4068,17 +4234,17 @@ __export(registry_exports, {
|
|
|
4068
4234
|
readRegistryRequired: () => readRegistryRequired
|
|
4069
4235
|
});
|
|
4070
4236
|
import { createHash as createHash4 } from "node:crypto";
|
|
4071
|
-
import { join as
|
|
4237
|
+
import { join as join23 } from "node:path";
|
|
4072
4238
|
import { mkdir as mkdir4, access, readFile as readFile4 } from "node:fs/promises";
|
|
4073
4239
|
import { z } from "zod";
|
|
4074
4240
|
function getNexusHome() {
|
|
4075
|
-
return process.env["NEXUS_HOME"] ??
|
|
4241
|
+
return process.env["NEXUS_HOME"] ?? join23(getCleoHome(), "nexus");
|
|
4076
4242
|
}
|
|
4077
4243
|
function getNexusCacheDir() {
|
|
4078
|
-
return process.env["NEXUS_CACHE_DIR"] ??
|
|
4244
|
+
return process.env["NEXUS_CACHE_DIR"] ?? join23(getNexusHome(), "cache");
|
|
4079
4245
|
}
|
|
4080
4246
|
function getRegistryPath() {
|
|
4081
|
-
return process.env["NEXUS_REGISTRY_FILE"] ??
|
|
4247
|
+
return process.env["NEXUS_REGISTRY_FILE"] ?? join23(getCleoHome(), "projects-registry.json");
|
|
4082
4248
|
}
|
|
4083
4249
|
function generateProjectHash(projectPath) {
|
|
4084
4250
|
const hash = createHash4("sha256").update(projectPath).digest("hex");
|
|
@@ -4120,7 +4286,7 @@ async function nexusInit() {
|
|
|
4120
4286
|
}
|
|
4121
4287
|
async function isCleoProject(projectPath) {
|
|
4122
4288
|
try {
|
|
4123
|
-
await access(
|
|
4289
|
+
await access(join23(projectPath, ".cleo", "todo.json"));
|
|
4124
4290
|
return true;
|
|
4125
4291
|
} catch {
|
|
4126
4292
|
return false;
|
|
@@ -4130,9 +4296,9 @@ async function readProjectMeta(projectPath) {
|
|
|
4130
4296
|
try {
|
|
4131
4297
|
let raw;
|
|
4132
4298
|
try {
|
|
4133
|
-
raw = await readFile4(
|
|
4299
|
+
raw = await readFile4(join23(projectPath, ".cleo", "tasks.json"), "utf-8");
|
|
4134
4300
|
} catch {
|
|
4135
|
-
raw = await readFile4(
|
|
4301
|
+
raw = await readFile4(join23(projectPath, ".cleo", "todo.json"), "utf-8");
|
|
4136
4302
|
}
|
|
4137
4303
|
const data = JSON.parse(raw);
|
|
4138
4304
|
const tasks2 = data.tasks ?? [];
|
|
@@ -4318,10 +4484,10 @@ var project_detect_exports = {};
|
|
|
4318
4484
|
__export(project_detect_exports, {
|
|
4319
4485
|
detectProjectType: () => detectProjectType
|
|
4320
4486
|
});
|
|
4321
|
-
import { existsSync as
|
|
4322
|
-
import { join as
|
|
4487
|
+
import { existsSync as existsSync22, readdirSync as readdirSync6 } from "node:fs";
|
|
4488
|
+
import { join as join24 } from "node:path";
|
|
4323
4489
|
function detectProjectType(projectDir) {
|
|
4324
|
-
const exists = (f) =>
|
|
4490
|
+
const exists = (f) => existsSync22(join24(projectDir, f));
|
|
4325
4491
|
const info = {
|
|
4326
4492
|
type: "unknown",
|
|
4327
4493
|
testFramework: "unknown",
|
|
@@ -4392,6 +4558,7 @@ __export(validation_schemas_exports, {
|
|
|
4392
4558
|
insertLifecyclePipelineSchema: () => insertLifecyclePipelineSchema,
|
|
4393
4559
|
insertLifecycleStageSchema: () => insertLifecycleStageSchema,
|
|
4394
4560
|
insertLifecycleTransitionSchema: () => insertLifecycleTransitionSchema,
|
|
4561
|
+
insertManifestEntrySchema: () => insertManifestEntrySchema,
|
|
4395
4562
|
insertSchemaMetaSchema: () => insertSchemaMetaSchema,
|
|
4396
4563
|
insertSessionSchema: () => insertSessionSchema,
|
|
4397
4564
|
insertTaskDependencySchema: () => insertTaskDependencySchema,
|
|
@@ -4405,6 +4572,7 @@ __export(validation_schemas_exports, {
|
|
|
4405
4572
|
selectLifecyclePipelineSchema: () => selectLifecyclePipelineSchema,
|
|
4406
4573
|
selectLifecycleStageSchema: () => selectLifecycleStageSchema,
|
|
4407
4574
|
selectLifecycleTransitionSchema: () => selectLifecycleTransitionSchema,
|
|
4575
|
+
selectManifestEntrySchema: () => selectManifestEntrySchema,
|
|
4408
4576
|
selectSchemaMetaSchema: () => selectSchemaMetaSchema,
|
|
4409
4577
|
selectSessionSchema: () => selectSessionSchema,
|
|
4410
4578
|
selectTaskDependencySchema: () => selectTaskDependencySchema,
|
|
@@ -4418,7 +4586,7 @@ __export(validation_schemas_exports, {
|
|
|
4418
4586
|
});
|
|
4419
4587
|
import { createInsertSchema, createSelectSchema } from "drizzle-orm/zod";
|
|
4420
4588
|
import { z as z2 } from "zod/v4";
|
|
4421
|
-
var taskRefinements, insertTaskSchema, selectTaskSchema, insertTaskDependencySchema, selectTaskDependencySchema, insertTaskRelationSchema, selectTaskRelationSchema, insertSessionSchema, selectSessionSchema, sessionScopeSchema, sessionStatsSchema, sessionTaskWorkSchema, sessionSchema, insertWorkHistorySchema, selectWorkHistorySchema, insertLifecyclePipelineSchema, selectLifecyclePipelineSchema, insertLifecycleStageSchema, selectLifecycleStageSchema, insertLifecycleGateResultSchema, selectLifecycleGateResultSchema, insertLifecycleEvidenceSchema, selectLifecycleEvidenceSchema, insertLifecycleTransitionSchema, selectLifecycleTransitionSchema, insertSchemaMetaSchema, selectSchemaMetaSchema, insertAuditLogSchema, AuditLogInsertSchema, selectAuditLogSchema, AuditLogSelectSchema, insertArchitectureDecisionSchema, selectArchitectureDecisionSchema;
|
|
4589
|
+
var taskRefinements, insertTaskSchema, selectTaskSchema, insertTaskDependencySchema, selectTaskDependencySchema, insertTaskRelationSchema, selectTaskRelationSchema, insertSessionSchema, selectSessionSchema, sessionScopeSchema, sessionStatsSchema, sessionTaskWorkSchema, sessionSchema, insertWorkHistorySchema, selectWorkHistorySchema, insertLifecyclePipelineSchema, selectLifecyclePipelineSchema, insertLifecycleStageSchema, selectLifecycleStageSchema, insertLifecycleGateResultSchema, selectLifecycleGateResultSchema, insertLifecycleEvidenceSchema, selectLifecycleEvidenceSchema, insertLifecycleTransitionSchema, selectLifecycleTransitionSchema, insertSchemaMetaSchema, selectSchemaMetaSchema, insertAuditLogSchema, AuditLogInsertSchema, selectAuditLogSchema, AuditLogSelectSchema, insertArchitectureDecisionSchema, selectArchitectureDecisionSchema, insertManifestEntrySchema, selectManifestEntrySchema;
|
|
4422
4590
|
var init_validation_schemas = __esm({
|
|
4423
4591
|
"src/store/validation-schemas.ts"() {
|
|
4424
4592
|
"use strict";
|
|
@@ -4511,6 +4679,8 @@ var init_validation_schemas = __esm({
|
|
|
4511
4679
|
AuditLogSelectSchema = selectAuditLogSchema;
|
|
4512
4680
|
insertArchitectureDecisionSchema = createInsertSchema(architectureDecisions);
|
|
4513
4681
|
selectArchitectureDecisionSchema = createSelectSchema(architectureDecisions);
|
|
4682
|
+
insertManifestEntrySchema = createInsertSchema(manifestEntries);
|
|
4683
|
+
selectManifestEntrySchema = createSelectSchema(manifestEntries);
|
|
4514
4684
|
}
|
|
4515
4685
|
});
|
|
4516
4686
|
|
|
@@ -4684,8 +4854,8 @@ __export(session_grade_exports, {
|
|
|
4684
4854
|
gradeSession: () => gradeSession,
|
|
4685
4855
|
readGrades: () => readGrades
|
|
4686
4856
|
});
|
|
4687
|
-
import { join as
|
|
4688
|
-
import { existsSync as
|
|
4857
|
+
import { join as join41 } from "node:path";
|
|
4858
|
+
import { existsSync as existsSync40 } from "node:fs";
|
|
4689
4859
|
import { readFile as readFile6, appendFile, mkdir as mkdir6 } from "node:fs/promises";
|
|
4690
4860
|
async function gradeSession(sessionId, cwd) {
|
|
4691
4861
|
const sessionEntries = await queryAudit({ sessionId });
|
|
@@ -4865,9 +5035,9 @@ function detectDuplicateCreates(entries) {
|
|
|
4865
5035
|
async function appendGradeResult(result, cwd) {
|
|
4866
5036
|
try {
|
|
4867
5037
|
const cleoDir = getCleoDirAbsolute(cwd);
|
|
4868
|
-
const metricsDir =
|
|
5038
|
+
const metricsDir = join41(cleoDir, "metrics");
|
|
4869
5039
|
await mkdir6(metricsDir, { recursive: true });
|
|
4870
|
-
const gradesPath =
|
|
5040
|
+
const gradesPath = join41(metricsDir, "GRADES.jsonl");
|
|
4871
5041
|
const line = JSON.stringify({ ...result, evaluator: "auto" }) + "\n";
|
|
4872
5042
|
await appendFile(gradesPath, line, "utf8");
|
|
4873
5043
|
} catch {
|
|
@@ -4876,8 +5046,8 @@ async function appendGradeResult(result, cwd) {
|
|
|
4876
5046
|
async function readGrades(sessionId, cwd) {
|
|
4877
5047
|
try {
|
|
4878
5048
|
const cleoDir = getCleoDirAbsolute(cwd);
|
|
4879
|
-
const gradesPath =
|
|
4880
|
-
if (!
|
|
5049
|
+
const gradesPath = join41(cleoDir, "metrics", "GRADES.jsonl");
|
|
5050
|
+
if (!existsSync40(gradesPath)) return [];
|
|
4881
5051
|
const content = await readFile6(gradesPath, "utf8");
|
|
4882
5052
|
const results = content.split("\n").filter((l) => l.trim()).map((l) => JSON.parse(l));
|
|
4883
5053
|
return sessionId ? results.filter((r) => r.sessionId === sessionId) : results;
|
|
@@ -5241,7 +5411,7 @@ if (actualQueryCount < 1) {
|
|
|
5241
5411
|
function registerQueryTool() {
|
|
5242
5412
|
return {
|
|
5243
5413
|
name: "cleo_query",
|
|
5244
|
-
description:
|
|
5414
|
+
description: 'CLEO read operations: task discovery, status checks, analysis, validation, and compliance metrics. Never modifies state. First call: use domain "admin", operation "help" to discover all available operations.',
|
|
5245
5415
|
inputSchema: {
|
|
5246
5416
|
type: "object",
|
|
5247
5417
|
required: ["domain", "operation"],
|
|
@@ -5253,7 +5423,7 @@ function registerQueryTool() {
|
|
|
5253
5423
|
},
|
|
5254
5424
|
operation: {
|
|
5255
5425
|
type: "string",
|
|
5256
|
-
description: "Domain-specific read operation
|
|
5426
|
+
description: "Domain-specific read operation. Call admin.help to see the full operation matrix. Common: tasks.find, tasks.show, tasks.next, session.status, admin.dash"
|
|
5257
5427
|
},
|
|
5258
5428
|
params: {
|
|
5259
5429
|
type: "object",
|
|
@@ -5563,7 +5733,7 @@ if (actualMutateCount < 1) {
|
|
|
5563
5733
|
function registerMutateTool() {
|
|
5564
5734
|
return {
|
|
5565
5735
|
name: "cleo_mutate",
|
|
5566
|
-
description:
|
|
5736
|
+
description: 'CLEO write operations: create, update, complete tasks; manage sessions; spawn agents; progress lifecycle; execute releases. Modifies state with validation. Use cleo_query with domain "admin", operation "help" first to discover available operations.',
|
|
5567
5737
|
inputSchema: {
|
|
5568
5738
|
type: "object",
|
|
5569
5739
|
required: ["domain", "operation"],
|
|
@@ -5575,7 +5745,7 @@ function registerMutateTool() {
|
|
|
5575
5745
|
},
|
|
5576
5746
|
operation: {
|
|
5577
5747
|
type: "string",
|
|
5578
|
-
description: "Domain-specific write operation
|
|
5748
|
+
description: "Domain-specific write operation. Call cleo_query admin.help to see the full operation matrix. Common: tasks.add, tasks.update, tasks.complete, session.start, session.end"
|
|
5579
5749
|
},
|
|
5580
5750
|
params: {
|
|
5581
5751
|
type: "object",
|
|
@@ -7813,7 +7983,6 @@ init_data_accessor();
|
|
|
7813
7983
|
|
|
7814
7984
|
// src/dispatch/engines/_error.ts
|
|
7815
7985
|
init_logger();
|
|
7816
|
-
var logger = getLogger("engine");
|
|
7817
7986
|
var STRING_TO_EXIT = {
|
|
7818
7987
|
// General Errors (1-9)
|
|
7819
7988
|
E_GENERAL: 1,
|
|
@@ -7942,6 +8111,7 @@ function logLevel(exitCode) {
|
|
|
7942
8111
|
function engineError(code, message, options) {
|
|
7943
8112
|
const exitCode = STRING_TO_EXIT[code] ?? 1;
|
|
7944
8113
|
const level = logLevel(exitCode);
|
|
8114
|
+
const logger = getLogger("engine");
|
|
7945
8115
|
logger[level]({ code, exitCode, ...options?.details && { details: options.details } }, message);
|
|
7946
8116
|
return {
|
|
7947
8117
|
success: false,
|
|
@@ -8564,6 +8734,7 @@ function validateHierarchyPlacement(parentId, tasks2, policy) {
|
|
|
8564
8734
|
}
|
|
8565
8735
|
|
|
8566
8736
|
// src/core/tasks/add.ts
|
|
8737
|
+
init_sequence();
|
|
8567
8738
|
function validateTitle(title) {
|
|
8568
8739
|
if (!title || title.trim().length === 0) {
|
|
8569
8740
|
throw new CleoError(2 /* INVALID_INPUT */, "Task title is required");
|
|
@@ -8746,20 +8917,23 @@ async function addTask(options, cwd, accessor) {
|
|
|
8746
8917
|
validateTitle(options.title);
|
|
8747
8918
|
const data = accessor ? await accessor.loadTaskFile() : await readJsonRequired(taskPath);
|
|
8748
8919
|
let archivedTasks = [];
|
|
8749
|
-
|
|
8750
|
-
|
|
8751
|
-
|
|
8752
|
-
if (
|
|
8753
|
-
|
|
8754
|
-
|
|
8755
|
-
|
|
8756
|
-
|
|
8757
|
-
|
|
8758
|
-
|
|
8759
|
-
|
|
8920
|
+
const useSqliteAlloc = accessor?.engine === "sqlite" || !accessor && (await Promise.resolve().then(() => (init_sqlite(), sqlite_exports))).getNativeDb() !== null;
|
|
8921
|
+
if (!useSqliteAlloc) {
|
|
8922
|
+
try {
|
|
8923
|
+
if (accessor) {
|
|
8924
|
+
const archive = await accessor.loadArchive();
|
|
8925
|
+
if (archive?.archivedTasks) {
|
|
8926
|
+
archivedTasks = archive.archivedTasks;
|
|
8927
|
+
}
|
|
8928
|
+
} else {
|
|
8929
|
+
const { readJson: readJson2 } = await Promise.resolve().then(() => (init_json(), json_exports));
|
|
8930
|
+
const archive = await readJson2(archivePath);
|
|
8931
|
+
if (archive?.archivedTasks) {
|
|
8932
|
+
archivedTasks = archive.archivedTasks;
|
|
8933
|
+
}
|
|
8760
8934
|
}
|
|
8935
|
+
} catch {
|
|
8761
8936
|
}
|
|
8762
|
-
} catch {
|
|
8763
8937
|
}
|
|
8764
8938
|
const status = options.status ?? "pending";
|
|
8765
8939
|
const priority = normalizePriority(options.priority ?? "medium");
|
|
@@ -8837,9 +9011,14 @@ async function addTask(options, cwd, accessor) {
|
|
|
8837
9011
|
if (duplicate) {
|
|
8838
9012
|
return { task: duplicate, duplicate: true };
|
|
8839
9013
|
}
|
|
8840
|
-
|
|
8841
|
-
if (
|
|
8842
|
-
|
|
9014
|
+
let taskId;
|
|
9015
|
+
if (useSqliteAlloc) {
|
|
9016
|
+
taskId = await allocateNextTaskId(cwd);
|
|
9017
|
+
} else {
|
|
9018
|
+
taskId = generateTaskId(data.tasks, archivedTasks);
|
|
9019
|
+
if (data.tasks.some((t) => t.id === taskId)) {
|
|
9020
|
+
throw new CleoError(22 /* ID_COLLISION */, `Generated ID ${taskId} already exists`);
|
|
9021
|
+
}
|
|
8843
9022
|
}
|
|
8844
9023
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
8845
9024
|
const position = options.position ?? getNextPosition(parentId, data.tasks);
|
|
@@ -9193,6 +9372,291 @@ async function updateTask2(options, cwd, accessor) {
|
|
|
9193
9372
|
return { task, changes };
|
|
9194
9373
|
}
|
|
9195
9374
|
|
|
9375
|
+
// src/core/tasks/complete.ts
|
|
9376
|
+
init_json();
|
|
9377
|
+
init_errors();
|
|
9378
|
+
init_exit_codes();
|
|
9379
|
+
init_paths();
|
|
9380
|
+
|
|
9381
|
+
// src/core/tasks/dependency-check.ts
|
|
9382
|
+
function detectCircularDeps(taskId, tasks2) {
|
|
9383
|
+
const taskMap = new Map(tasks2.map((t) => [t.id, t]));
|
|
9384
|
+
const visited = /* @__PURE__ */ new Set();
|
|
9385
|
+
const recursionStack = /* @__PURE__ */ new Set();
|
|
9386
|
+
const path = [];
|
|
9387
|
+
function dfs(id) {
|
|
9388
|
+
visited.add(id);
|
|
9389
|
+
recursionStack.add(id);
|
|
9390
|
+
path.push(id);
|
|
9391
|
+
const task = taskMap.get(id);
|
|
9392
|
+
if (task?.depends) {
|
|
9393
|
+
for (const depId of task.depends) {
|
|
9394
|
+
if (!visited.has(depId)) {
|
|
9395
|
+
const cycle = dfs(depId);
|
|
9396
|
+
if (cycle.length > 0) return cycle;
|
|
9397
|
+
} else if (recursionStack.has(depId)) {
|
|
9398
|
+
const cycleStart = path.indexOf(depId);
|
|
9399
|
+
return [...path.slice(cycleStart), depId];
|
|
9400
|
+
}
|
|
9401
|
+
}
|
|
9402
|
+
}
|
|
9403
|
+
path.pop();
|
|
9404
|
+
recursionStack.delete(id);
|
|
9405
|
+
return [];
|
|
9406
|
+
}
|
|
9407
|
+
return dfs(taskId);
|
|
9408
|
+
}
|
|
9409
|
+
function getBlockedTasks(tasks2) {
|
|
9410
|
+
const completedIds = new Set(
|
|
9411
|
+
tasks2.filter((t) => t.status === "done" || t.status === "cancelled").map((t) => t.id)
|
|
9412
|
+
);
|
|
9413
|
+
return tasks2.filter((t) => {
|
|
9414
|
+
if (!t.depends?.length) return false;
|
|
9415
|
+
if (t.status === "done" || t.status === "cancelled") return false;
|
|
9416
|
+
return t.depends.some((depId) => !completedIds.has(depId));
|
|
9417
|
+
});
|
|
9418
|
+
}
|
|
9419
|
+
function getReadyTasks(tasks2) {
|
|
9420
|
+
const completedIds = new Set(
|
|
9421
|
+
tasks2.filter((t) => t.status === "done" || t.status === "cancelled").map((t) => t.id)
|
|
9422
|
+
);
|
|
9423
|
+
return tasks2.filter((t) => {
|
|
9424
|
+
if (t.status === "done" || t.status === "cancelled") return false;
|
|
9425
|
+
if (!t.depends?.length) return true;
|
|
9426
|
+
return t.depends.every((depId) => completedIds.has(depId));
|
|
9427
|
+
});
|
|
9428
|
+
}
|
|
9429
|
+
function getDependents(taskId, tasks2) {
|
|
9430
|
+
return tasks2.filter((t) => t.depends?.includes(taskId));
|
|
9431
|
+
}
|
|
9432
|
+
function getUnresolvedDeps(taskId, tasks2) {
|
|
9433
|
+
const task = tasks2.find((t) => t.id === taskId);
|
|
9434
|
+
if (!task?.depends?.length) return [];
|
|
9435
|
+
const completedIds = new Set(
|
|
9436
|
+
tasks2.filter((t) => t.status === "done" || t.status === "cancelled").map((t) => t.id)
|
|
9437
|
+
);
|
|
9438
|
+
return task.depends.filter((depId) => !completedIds.has(depId));
|
|
9439
|
+
}
|
|
9440
|
+
function validateDependencyRefs(tasks2) {
|
|
9441
|
+
const taskIds = new Set(tasks2.map((t) => t.id));
|
|
9442
|
+
const errors = [];
|
|
9443
|
+
for (const task of tasks2) {
|
|
9444
|
+
if (!task.depends?.length) continue;
|
|
9445
|
+
for (const depId of task.depends) {
|
|
9446
|
+
if (!taskIds.has(depId)) {
|
|
9447
|
+
errors.push({
|
|
9448
|
+
code: "E_DEP_NOT_FOUND",
|
|
9449
|
+
taskId: task.id,
|
|
9450
|
+
message: `Task ${task.id} depends on ${depId}, which does not exist`,
|
|
9451
|
+
relatedIds: [depId]
|
|
9452
|
+
});
|
|
9453
|
+
}
|
|
9454
|
+
}
|
|
9455
|
+
}
|
|
9456
|
+
return errors;
|
|
9457
|
+
}
|
|
9458
|
+
function validateDependencies(tasks2) {
|
|
9459
|
+
const errors = [];
|
|
9460
|
+
const warnings = [];
|
|
9461
|
+
errors.push(...validateDependencyRefs(tasks2));
|
|
9462
|
+
const visited = /* @__PURE__ */ new Set();
|
|
9463
|
+
for (const task of tasks2) {
|
|
9464
|
+
if (visited.has(task.id)) continue;
|
|
9465
|
+
if (!task.depends?.length) continue;
|
|
9466
|
+
const cycle = detectCircularDeps(task.id, tasks2);
|
|
9467
|
+
if (cycle.length > 0) {
|
|
9468
|
+
errors.push({
|
|
9469
|
+
code: "E_CIRCULAR_DEP",
|
|
9470
|
+
taskId: task.id,
|
|
9471
|
+
message: `Circular dependency detected: ${cycle.join(" -> ")}`,
|
|
9472
|
+
relatedIds: cycle
|
|
9473
|
+
});
|
|
9474
|
+
cycle.forEach((id) => visited.add(id));
|
|
9475
|
+
}
|
|
9476
|
+
}
|
|
9477
|
+
for (const task of tasks2) {
|
|
9478
|
+
if (task.depends?.includes(task.id)) {
|
|
9479
|
+
errors.push({
|
|
9480
|
+
code: "E_SELF_DEP",
|
|
9481
|
+
taskId: task.id,
|
|
9482
|
+
message: `Task ${task.id} depends on itself`
|
|
9483
|
+
});
|
|
9484
|
+
}
|
|
9485
|
+
}
|
|
9486
|
+
for (const task of tasks2) {
|
|
9487
|
+
if (task.status === "done" && task.depends?.length) {
|
|
9488
|
+
const unresolved = getUnresolvedDeps(task.id, tasks2);
|
|
9489
|
+
if (unresolved.length > 0) {
|
|
9490
|
+
warnings.push({
|
|
9491
|
+
code: "W_COMPLETED_WITH_UNMET_DEPS",
|
|
9492
|
+
taskId: task.id,
|
|
9493
|
+
message: `Completed task ${task.id} has unmet dependencies: ${unresolved.join(", ")}`
|
|
9494
|
+
});
|
|
9495
|
+
}
|
|
9496
|
+
}
|
|
9497
|
+
}
|
|
9498
|
+
return {
|
|
9499
|
+
valid: errors.length === 0,
|
|
9500
|
+
errors,
|
|
9501
|
+
warnings
|
|
9502
|
+
};
|
|
9503
|
+
}
|
|
9504
|
+
function getTransitiveBlockers(taskId, tasks2) {
|
|
9505
|
+
const taskMap = new Map(tasks2.map((t) => [t.id, t]));
|
|
9506
|
+
const task = taskMap.get(taskId);
|
|
9507
|
+
if (!task?.depends?.length) return [];
|
|
9508
|
+
const blockers = /* @__PURE__ */ new Set();
|
|
9509
|
+
const visited = /* @__PURE__ */ new Set();
|
|
9510
|
+
function walk(id) {
|
|
9511
|
+
if (visited.has(id)) return;
|
|
9512
|
+
visited.add(id);
|
|
9513
|
+
const t = taskMap.get(id);
|
|
9514
|
+
if (!t?.depends?.length) return;
|
|
9515
|
+
for (const depId of t.depends) {
|
|
9516
|
+
const dep = taskMap.get(depId);
|
|
9517
|
+
if (!dep) continue;
|
|
9518
|
+
if (dep.status === "done" || dep.status === "cancelled") continue;
|
|
9519
|
+
blockers.add(depId);
|
|
9520
|
+
walk(depId);
|
|
9521
|
+
}
|
|
9522
|
+
}
|
|
9523
|
+
walk(taskId);
|
|
9524
|
+
return [...blockers];
|
|
9525
|
+
}
|
|
9526
|
+
function getLeafBlockers(taskId, tasks2) {
|
|
9527
|
+
const blockerIds = getTransitiveBlockers(taskId, tasks2);
|
|
9528
|
+
if (blockerIds.length === 0) return [];
|
|
9529
|
+
const taskMap = new Map(tasks2.map((t) => [t.id, t]));
|
|
9530
|
+
const completedIds = new Set(
|
|
9531
|
+
tasks2.filter((t) => t.status === "done" || t.status === "cancelled").map((t) => t.id)
|
|
9532
|
+
);
|
|
9533
|
+
return blockerIds.filter((id) => {
|
|
9534
|
+
const t = taskMap.get(id);
|
|
9535
|
+
if (!t?.depends?.length) return true;
|
|
9536
|
+
return t.depends.every((depId) => completedIds.has(depId));
|
|
9537
|
+
});
|
|
9538
|
+
}
|
|
9539
|
+
|
|
9540
|
+
// src/core/tasks/complete.ts
|
|
9541
|
+
init_data_safety_central();
|
|
9542
|
+
async function completeTask(options, cwd, accessor) {
|
|
9543
|
+
const taskPath = getTaskPath(cwd);
|
|
9544
|
+
const logPath = getLogPath(cwd);
|
|
9545
|
+
const backupDir = getBackupDir(cwd);
|
|
9546
|
+
const data = accessor ? await accessor.loadTaskFile() : await readJsonRequired(taskPath);
|
|
9547
|
+
const taskIdx = data.tasks.findIndex((t) => t.id === options.taskId);
|
|
9548
|
+
if (taskIdx === -1) {
|
|
9549
|
+
throw new CleoError(
|
|
9550
|
+
4 /* NOT_FOUND */,
|
|
9551
|
+
`Task not found: ${options.taskId}`,
|
|
9552
|
+
{ fix: `Use 'cleo find "${options.taskId}"' to search` }
|
|
9553
|
+
);
|
|
9554
|
+
}
|
|
9555
|
+
const task = data.tasks[taskIdx];
|
|
9556
|
+
if (task.status === "done") {
|
|
9557
|
+
throw new CleoError(
|
|
9558
|
+
17 /* TASK_COMPLETED */,
|
|
9559
|
+
`Task ${options.taskId} is already completed`
|
|
9560
|
+
);
|
|
9561
|
+
}
|
|
9562
|
+
if (task.depends?.length) {
|
|
9563
|
+
const incompleteDeps = task.depends.filter((depId) => {
|
|
9564
|
+
const dep = data.tasks.find((t) => t.id === depId);
|
|
9565
|
+
return dep && dep.status !== "done";
|
|
9566
|
+
});
|
|
9567
|
+
if (incompleteDeps.length > 0) {
|
|
9568
|
+
throw new CleoError(
|
|
9569
|
+
5 /* DEPENDENCY_ERROR */,
|
|
9570
|
+
`Task ${options.taskId} has incomplete dependencies: ${incompleteDeps.join(", ")}`,
|
|
9571
|
+
{ fix: `Complete dependencies first: ${incompleteDeps.map((d) => `cleo complete ${d}`).join(", ")}` }
|
|
9572
|
+
);
|
|
9573
|
+
}
|
|
9574
|
+
}
|
|
9575
|
+
const children = data.tasks.filter((t) => t.parentId === options.taskId);
|
|
9576
|
+
const incompleteChildren = children.filter((c) => c.status !== "done" && c.status !== "cancelled");
|
|
9577
|
+
if (incompleteChildren.length > 0 && task.type === "epic") {
|
|
9578
|
+
if (!task.noAutoComplete) {
|
|
9579
|
+
throw new CleoError(
|
|
9580
|
+
16 /* HAS_CHILDREN */,
|
|
9581
|
+
`Epic ${options.taskId} has ${incompleteChildren.length} incomplete children: ${incompleteChildren.map((c) => c.id).join(", ")}`,
|
|
9582
|
+
{ fix: `Complete children first or use 'cleo update ${options.taskId} --no-auto-complete'` }
|
|
9583
|
+
);
|
|
9584
|
+
}
|
|
9585
|
+
}
|
|
9586
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
9587
|
+
const before = { ...task };
|
|
9588
|
+
task.status = "done";
|
|
9589
|
+
task.completedAt = now;
|
|
9590
|
+
task.updatedAt = now;
|
|
9591
|
+
if (options.notes) {
|
|
9592
|
+
const timestampedNote = `${(/* @__PURE__ */ new Date()).toISOString().replace("T", " ").replace(/\.\d+Z$/, " UTC")}: ${options.notes}`;
|
|
9593
|
+
if (!task.notes) task.notes = [];
|
|
9594
|
+
task.notes.push(timestampedNote);
|
|
9595
|
+
}
|
|
9596
|
+
if (options.changeset) {
|
|
9597
|
+
if (!task.notes) task.notes = [];
|
|
9598
|
+
task.notes.push(`Changeset: ${options.changeset}`);
|
|
9599
|
+
}
|
|
9600
|
+
data.tasks[taskIdx] = task;
|
|
9601
|
+
const autoCompleted = [];
|
|
9602
|
+
if (task.parentId) {
|
|
9603
|
+
const parent = data.tasks.find((t) => t.id === task.parentId);
|
|
9604
|
+
if (parent && parent.type === "epic" && !parent.noAutoComplete) {
|
|
9605
|
+
const parentChildren = data.tasks.filter((t) => t.parentId === parent.id);
|
|
9606
|
+
const allDone = parentChildren.every((c) => c.status === "done" || c.status === "cancelled");
|
|
9607
|
+
if (allDone) {
|
|
9608
|
+
parent.status = "done";
|
|
9609
|
+
parent.completedAt = now;
|
|
9610
|
+
parent.updatedAt = now;
|
|
9611
|
+
autoCompleted.push(parent.id);
|
|
9612
|
+
}
|
|
9613
|
+
}
|
|
9614
|
+
}
|
|
9615
|
+
data._meta.checksum = computeChecksum(data.tasks);
|
|
9616
|
+
data.lastUpdated = now;
|
|
9617
|
+
if (accessor) {
|
|
9618
|
+
if (accessor.upsertSingleTask) {
|
|
9619
|
+
await accessor.upsertSingleTask(task);
|
|
9620
|
+
for (const parentId of autoCompleted) {
|
|
9621
|
+
const parent = data.tasks.find((t) => t.id === parentId);
|
|
9622
|
+
if (parent) await accessor.upsertSingleTask(parent);
|
|
9623
|
+
}
|
|
9624
|
+
} else {
|
|
9625
|
+
await safeSaveTaskFile(accessor, data, cwd);
|
|
9626
|
+
}
|
|
9627
|
+
await safeAppendLog(accessor, {
|
|
9628
|
+
id: `log-${Math.floor(Date.now() / 1e3)}-${(await import("node:crypto")).randomBytes(3).toString("hex")}`,
|
|
9629
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9630
|
+
action: "task_completed",
|
|
9631
|
+
taskId: options.taskId,
|
|
9632
|
+
actor: "system",
|
|
9633
|
+
details: { title: task.title, previousStatus: before.status },
|
|
9634
|
+
before: null,
|
|
9635
|
+
after: { title: task.title, previousStatus: before.status }
|
|
9636
|
+
}, cwd);
|
|
9637
|
+
} else {
|
|
9638
|
+
await saveJson(taskPath, data, { backupDir });
|
|
9639
|
+
await logOperation(logPath, "task_completed", options.taskId, {
|
|
9640
|
+
title: task.title,
|
|
9641
|
+
previousStatus: before.status
|
|
9642
|
+
});
|
|
9643
|
+
}
|
|
9644
|
+
const dependents = getDependents(options.taskId, data.tasks);
|
|
9645
|
+
const unblockedTasks = [];
|
|
9646
|
+
for (const dep of dependents) {
|
|
9647
|
+
if (dep.status === "done" || dep.status === "cancelled") continue;
|
|
9648
|
+
const stillUnresolved = getUnresolvedDeps(dep.id, data.tasks);
|
|
9649
|
+
if (stillUnresolved.length === 0) {
|
|
9650
|
+
unblockedTasks.push({ id: dep.id, title: dep.title });
|
|
9651
|
+
}
|
|
9652
|
+
}
|
|
9653
|
+
return {
|
|
9654
|
+
task,
|
|
9655
|
+
...autoCompleted.length > 0 && { autoCompleted },
|
|
9656
|
+
...unblockedTasks.length > 0 && { unblockedTasks }
|
|
9657
|
+
};
|
|
9658
|
+
}
|
|
9659
|
+
|
|
9196
9660
|
// src/core/tasks/delete.ts
|
|
9197
9661
|
init_json();
|
|
9198
9662
|
init_errors();
|
|
@@ -9498,6 +9962,16 @@ async function showTask(taskId, cwd, accessor) {
|
|
|
9498
9962
|
title: dep?.title ?? "Unknown task"
|
|
9499
9963
|
};
|
|
9500
9964
|
});
|
|
9965
|
+
const unresolved = detail.dependencyStatus.filter(
|
|
9966
|
+
(d) => d.status !== "done" && d.status !== "cancelled"
|
|
9967
|
+
);
|
|
9968
|
+
if (unresolved.length > 0) {
|
|
9969
|
+
detail.unresolvedDeps = unresolved;
|
|
9970
|
+
}
|
|
9971
|
+
}
|
|
9972
|
+
const dependents = data.tasks.filter((t) => t.depends?.includes(taskId)).map((t) => t.id);
|
|
9973
|
+
if (dependents.length > 0) {
|
|
9974
|
+
detail.dependents = dependents;
|
|
9501
9975
|
}
|
|
9502
9976
|
const path = [taskId];
|
|
9503
9977
|
let currentId = task.parentId;
|
|
@@ -9518,6 +9992,16 @@ async function showTask(taskId, cwd, accessor) {
|
|
|
9518
9992
|
// src/core/tasks/list.ts
|
|
9519
9993
|
init_json();
|
|
9520
9994
|
init_paths();
|
|
9995
|
+
function toCompact(task) {
|
|
9996
|
+
return {
|
|
9997
|
+
id: task.id,
|
|
9998
|
+
title: task.title,
|
|
9999
|
+
status: task.status,
|
|
10000
|
+
priority: task.priority,
|
|
10001
|
+
type: task.type,
|
|
10002
|
+
parentId: task.parentId
|
|
10003
|
+
};
|
|
10004
|
+
}
|
|
9521
10005
|
async function listTasks(options = {}, cwd, accessor) {
|
|
9522
10006
|
const taskPath = getTaskPath(cwd);
|
|
9523
10007
|
const data = accessor ? await accessor.loadTaskFile() : await readJsonRequired(taskPath);
|
|
@@ -9694,34 +10178,19 @@ async function findTasks(options, cwd, accessor) {
|
|
|
9694
10178
|
|
|
9695
10179
|
// src/core/tasks/task-ops.ts
|
|
9696
10180
|
init_data_accessor();
|
|
9697
|
-
|
|
10181
|
+
init_file_utils();
|
|
9698
10182
|
init_status_registry();
|
|
9699
10183
|
init_deps_ready();
|
|
9700
10184
|
var PRIORITY_SCORE = {
|
|
9701
10185
|
critical: 100,
|
|
9702
10186
|
high: 75,
|
|
9703
|
-
medium: 50,
|
|
9704
|
-
low: 25
|
|
9705
|
-
};
|
|
9706
|
-
async function loadAllTasks2(projectRoot) {
|
|
9707
|
-
const accessor = await getAccessor(projectRoot);
|
|
9708
|
-
const data = await accessor.loadTaskFile();
|
|
9709
|
-
return data.tasks;
|
|
9710
|
-
}
|
|
9711
|
-
function buildBlockingChain(task, taskMap, visited = /* @__PURE__ */ new Set()) {
|
|
9712
|
-
const chain = [];
|
|
9713
|
-
if (visited.has(task.id)) return chain;
|
|
9714
|
-
visited.add(task.id);
|
|
9715
|
-
if (task.depends) {
|
|
9716
|
-
for (const depId of task.depends) {
|
|
9717
|
-
const dep = taskMap.get(depId);
|
|
9718
|
-
if (dep && dep.status !== "done" && dep.status !== "cancelled") {
|
|
9719
|
-
chain.push(depId);
|
|
9720
|
-
chain.push(...buildBlockingChain(dep, taskMap, visited));
|
|
9721
|
-
}
|
|
9722
|
-
}
|
|
9723
|
-
}
|
|
9724
|
-
return chain;
|
|
10187
|
+
medium: 50,
|
|
10188
|
+
low: 25
|
|
10189
|
+
};
|
|
10190
|
+
async function loadAllTasks2(projectRoot) {
|
|
10191
|
+
const accessor = await getAccessor(projectRoot);
|
|
10192
|
+
const data = await accessor.loadTaskFile();
|
|
10193
|
+
return data.tasks;
|
|
9725
10194
|
}
|
|
9726
10195
|
function buildTreeNode(task, childrenMap) {
|
|
9727
10196
|
const children = (childrenMap.get(task.id) ?? []).map(
|
|
@@ -9735,6 +10204,25 @@ function buildTreeNode(task, childrenMap) {
|
|
|
9735
10204
|
children
|
|
9736
10205
|
};
|
|
9737
10206
|
}
|
|
10207
|
+
function buildUpstreamTree(taskId, taskMap, visited = /* @__PURE__ */ new Set()) {
|
|
10208
|
+
const task = taskMap.get(taskId);
|
|
10209
|
+
if (!task?.depends?.length) return [];
|
|
10210
|
+
const nodes = [];
|
|
10211
|
+
for (const depId of task.depends) {
|
|
10212
|
+
if (visited.has(depId)) continue;
|
|
10213
|
+
visited.add(depId);
|
|
10214
|
+
const dep = taskMap.get(depId);
|
|
10215
|
+
if (!dep) continue;
|
|
10216
|
+
nodes.push({
|
|
10217
|
+
id: dep.id,
|
|
10218
|
+
title: dep.title,
|
|
10219
|
+
status: dep.status,
|
|
10220
|
+
type: dep.type,
|
|
10221
|
+
children: buildUpstreamTree(depId, taskMap, visited)
|
|
10222
|
+
});
|
|
10223
|
+
}
|
|
10224
|
+
return nodes;
|
|
10225
|
+
}
|
|
9738
10226
|
function countNodes(nodes) {
|
|
9739
10227
|
let count2 = nodes.length;
|
|
9740
10228
|
for (const node of nodes) {
|
|
@@ -9827,20 +10315,21 @@ async function coreTaskBlockers(projectRoot, params) {
|
|
|
9827
10315
|
return dep && dep.status !== "done" && dep.status !== "cancelled";
|
|
9828
10316
|
})
|
|
9829
10317
|
);
|
|
10318
|
+
const tasksAsTask = allTasks;
|
|
9830
10319
|
const blockerInfos = [
|
|
9831
10320
|
...blockedTasks.map((t) => ({
|
|
9832
10321
|
id: t.id,
|
|
9833
10322
|
title: t.title,
|
|
9834
10323
|
status: t.status,
|
|
9835
10324
|
depends: t.depends,
|
|
9836
|
-
blockingChain: analyze ?
|
|
10325
|
+
blockingChain: analyze ? getTransitiveBlockers(t.id, tasksAsTask) : []
|
|
9837
10326
|
})),
|
|
9838
10327
|
...depBlockedTasks.filter((t) => !blockedTasks.some((bt) => bt.id === t.id)).map((t) => ({
|
|
9839
10328
|
id: t.id,
|
|
9840
10329
|
title: t.title,
|
|
9841
10330
|
status: t.status,
|
|
9842
10331
|
depends: t.depends,
|
|
9843
|
-
blockingChain: analyze ?
|
|
10332
|
+
blockingChain: analyze ? getTransitiveBlockers(t.id, tasksAsTask) : []
|
|
9844
10333
|
}))
|
|
9845
10334
|
];
|
|
9846
10335
|
const blockerCounts = /* @__PURE__ */ new Map();
|
|
@@ -9922,7 +10411,10 @@ async function coreTaskRelatesAdd(projectRoot, taskId, relatedId, type, reason)
|
|
|
9922
10411
|
} else {
|
|
9923
10412
|
await accessor.saveTaskFile(current);
|
|
9924
10413
|
}
|
|
9925
|
-
|
|
10414
|
+
if (accessor.addRelation) {
|
|
10415
|
+
await accessor.addRelation(taskId, relatedId, type, reason);
|
|
10416
|
+
}
|
|
10417
|
+
return { from: taskId, to: relatedId, type, reason, added: true };
|
|
9926
10418
|
}
|
|
9927
10419
|
async function coreTaskAnalyze(projectRoot, taskId) {
|
|
9928
10420
|
const allTasks = await loadAllTasks2(projectRoot);
|
|
@@ -10242,7 +10734,63 @@ async function coreTaskComplexityEstimate(projectRoot, params) {
|
|
|
10242
10734
|
else size = "large";
|
|
10243
10735
|
return { size, score, factors, dependencyDepth, subtaskCount, fileCount };
|
|
10244
10736
|
}
|
|
10245
|
-
async function
|
|
10737
|
+
async function coreTaskDepsOverview(projectRoot) {
|
|
10738
|
+
const allTasks = await loadAllTasks2(projectRoot);
|
|
10739
|
+
const tasksAsTask = allTasks;
|
|
10740
|
+
const tasksWithDeps = allTasks.filter((t) => t.depends && t.depends.length > 0);
|
|
10741
|
+
const blocked = getBlockedTasks(tasksAsTask);
|
|
10742
|
+
const ready = getReadyTasks(tasksAsTask);
|
|
10743
|
+
const validation = validateDependencies(tasksAsTask);
|
|
10744
|
+
return {
|
|
10745
|
+
totalTasks: allTasks.length,
|
|
10746
|
+
tasksWithDeps: tasksWithDeps.length,
|
|
10747
|
+
blockedTasks: blocked.map((t) => ({
|
|
10748
|
+
id: t.id,
|
|
10749
|
+
title: t.title,
|
|
10750
|
+
status: t.status,
|
|
10751
|
+
unblockedBy: (t.depends ?? []).filter((depId) => {
|
|
10752
|
+
const dep = allTasks.find((x) => x.id === depId);
|
|
10753
|
+
return dep && dep.status !== "done" && dep.status !== "cancelled";
|
|
10754
|
+
})
|
|
10755
|
+
})),
|
|
10756
|
+
readyTasks: ready.filter((t) => t.status !== "done" && t.status !== "cancelled").map((t) => ({
|
|
10757
|
+
id: t.id,
|
|
10758
|
+
title: t.title,
|
|
10759
|
+
status: t.status
|
|
10760
|
+
})),
|
|
10761
|
+
validation: {
|
|
10762
|
+
valid: validation.valid,
|
|
10763
|
+
errorCount: validation.errors.length,
|
|
10764
|
+
warningCount: validation.warnings.length
|
|
10765
|
+
}
|
|
10766
|
+
};
|
|
10767
|
+
}
|
|
10768
|
+
async function coreTaskDepsCycles(projectRoot) {
|
|
10769
|
+
const allTasks = await loadAllTasks2(projectRoot);
|
|
10770
|
+
const tasksAsTask = allTasks;
|
|
10771
|
+
const taskMap = new Map(allTasks.map((t) => [t.id, t]));
|
|
10772
|
+
const visited = /* @__PURE__ */ new Set();
|
|
10773
|
+
const cycles = [];
|
|
10774
|
+
for (const task of allTasks) {
|
|
10775
|
+
if (visited.has(task.id)) continue;
|
|
10776
|
+
if (!task.depends?.length) continue;
|
|
10777
|
+
const cycle = detectCircularDeps(task.id, tasksAsTask);
|
|
10778
|
+
if (cycle.length > 0) {
|
|
10779
|
+
cycles.push({
|
|
10780
|
+
path: cycle,
|
|
10781
|
+
// Deduplicate: detectCircularDeps returns [A,B,C,A] where
|
|
10782
|
+
// last element closes the cycle. Use Set for robustness.
|
|
10783
|
+
tasks: [...new Set(cycle)].map((id) => {
|
|
10784
|
+
const t = taskMap.get(id);
|
|
10785
|
+
return { id, title: t?.title ?? "unknown" };
|
|
10786
|
+
})
|
|
10787
|
+
});
|
|
10788
|
+
cycle.forEach((id) => visited.add(id));
|
|
10789
|
+
}
|
|
10790
|
+
}
|
|
10791
|
+
return { hasCycles: cycles.length > 0, cycles };
|
|
10792
|
+
}
|
|
10793
|
+
async function coreTaskDepends(projectRoot, taskId, direction = "both", options) {
|
|
10246
10794
|
const allTasks = await loadAllTasks2(projectRoot);
|
|
10247
10795
|
const task = allTasks.find((t) => t.id === taskId);
|
|
10248
10796
|
if (!task) {
|
|
@@ -10266,7 +10814,31 @@ async function coreTaskDepends(projectRoot, taskId, direction = "both") {
|
|
|
10266
10814
|
}
|
|
10267
10815
|
}
|
|
10268
10816
|
}
|
|
10269
|
-
|
|
10817
|
+
const tasksAsTask = allTasks;
|
|
10818
|
+
const transitiveIds = getTransitiveBlockers(taskId, tasksAsTask);
|
|
10819
|
+
const unresolvedChain = transitiveIds.length;
|
|
10820
|
+
const leafIds = getLeafBlockers(taskId, tasksAsTask);
|
|
10821
|
+
const leafBlockers = leafIds.map((id) => {
|
|
10822
|
+
const t = taskMap.get(id);
|
|
10823
|
+
return { id: t.id, title: t.title, status: t.status };
|
|
10824
|
+
});
|
|
10825
|
+
const allDepsReady = unresolvedChain === 0;
|
|
10826
|
+
const hint = unresolvedChain > 0 ? `Run 'ct deps show ${taskId} --tree' for full dependency graph` : void 0;
|
|
10827
|
+
let upstreamTree;
|
|
10828
|
+
if (options?.tree) {
|
|
10829
|
+
upstreamTree = buildUpstreamTree(taskId, taskMap);
|
|
10830
|
+
}
|
|
10831
|
+
return {
|
|
10832
|
+
taskId,
|
|
10833
|
+
direction,
|
|
10834
|
+
upstream,
|
|
10835
|
+
downstream,
|
|
10836
|
+
unresolvedChain,
|
|
10837
|
+
leafBlockers,
|
|
10838
|
+
allDepsReady,
|
|
10839
|
+
...hint && { hint },
|
|
10840
|
+
...upstreamTree && { upstreamTree }
|
|
10841
|
+
};
|
|
10270
10842
|
}
|
|
10271
10843
|
|
|
10272
10844
|
// src/dispatch/engines/task-engine.ts
|
|
@@ -10300,17 +10872,25 @@ async function taskList(projectRoot, params) {
|
|
|
10300
10872
|
status: params?.status,
|
|
10301
10873
|
limit: params?.limit
|
|
10302
10874
|
}, projectRoot, accessor);
|
|
10875
|
+
if (params?.compact) {
|
|
10876
|
+
return { success: true, data: { tasks: result.tasks.map((t) => toCompact(t)), total: result.total } };
|
|
10877
|
+
}
|
|
10303
10878
|
return { success: true, data: { tasks: tasksToRecords(result.tasks), total: result.total } };
|
|
10304
10879
|
} catch {
|
|
10305
10880
|
return engineError("E_NOT_INITIALIZED", "Task database not initialized");
|
|
10306
10881
|
}
|
|
10307
10882
|
}
|
|
10308
|
-
async function taskFind(projectRoot, query, limit) {
|
|
10883
|
+
async function taskFind(projectRoot, query, limit, options) {
|
|
10309
10884
|
try {
|
|
10310
10885
|
const accessor = await getAccessor(projectRoot);
|
|
10311
10886
|
const findResult = await findTasks({
|
|
10312
10887
|
query,
|
|
10313
|
-
|
|
10888
|
+
id: options?.id,
|
|
10889
|
+
exact: options?.exact,
|
|
10890
|
+
status: options?.status,
|
|
10891
|
+
includeArchive: options?.includeArchive,
|
|
10892
|
+
limit: limit ?? 20,
|
|
10893
|
+
offset: options?.offset
|
|
10314
10894
|
}, projectRoot, accessor);
|
|
10315
10895
|
const results = findResult.results.map((r) => ({
|
|
10316
10896
|
id: r.id,
|
|
@@ -10413,14 +10993,33 @@ async function taskUpdate(projectRoot, taskId, updates) {
|
|
|
10413
10993
|
}
|
|
10414
10994
|
}
|
|
10415
10995
|
async function taskComplete(projectRoot, taskId, notes) {
|
|
10416
|
-
|
|
10417
|
-
|
|
10418
|
-
|
|
10996
|
+
try {
|
|
10997
|
+
const accessor = await getAccessor(projectRoot);
|
|
10998
|
+
const result = await completeTask({ taskId, notes }, void 0, accessor);
|
|
10999
|
+
return {
|
|
11000
|
+
success: true,
|
|
11001
|
+
data: {
|
|
11002
|
+
task: result.task,
|
|
11003
|
+
...result.autoCompleted && { autoCompleted: result.autoCompleted },
|
|
11004
|
+
...result.unblockedTasks && { unblockedTasks: result.unblockedTasks }
|
|
11005
|
+
}
|
|
11006
|
+
};
|
|
11007
|
+
} catch (err) {
|
|
11008
|
+
const message = err.message;
|
|
11009
|
+
if (message.includes("already completed")) {
|
|
11010
|
+
return engineError("E_TASK_COMPLETED", message);
|
|
11011
|
+
}
|
|
11012
|
+
if (message.includes("not found")) {
|
|
11013
|
+
return engineError("E_NOT_FOUND", message);
|
|
11014
|
+
}
|
|
11015
|
+
if (message.includes("incomplete dependencies")) {
|
|
11016
|
+
return engineError("E_DEPENDENCY_ERROR", message);
|
|
11017
|
+
}
|
|
11018
|
+
if (message.includes("incomplete children")) {
|
|
11019
|
+
return engineError("E_HAS_CHILDREN", message);
|
|
11020
|
+
}
|
|
11021
|
+
return engineError("E_INTERNAL", message);
|
|
10419
11022
|
}
|
|
10420
|
-
return taskUpdate(projectRoot, taskId, {
|
|
10421
|
-
status: "done",
|
|
10422
|
-
notes: notes || void 0
|
|
10423
|
-
});
|
|
10424
11023
|
}
|
|
10425
11024
|
async function taskDelete(projectRoot, taskId, force) {
|
|
10426
11025
|
try {
|
|
@@ -10625,9 +11224,9 @@ async function taskComplexityEstimate(projectRoot, params) {
|
|
|
10625
11224
|
return engineError("E_NOT_INITIALIZED", "Task database not initialized");
|
|
10626
11225
|
}
|
|
10627
11226
|
}
|
|
10628
|
-
async function taskDepends(projectRoot, taskId, direction = "both") {
|
|
11227
|
+
async function taskDepends(projectRoot, taskId, direction = "both", tree) {
|
|
10629
11228
|
try {
|
|
10630
|
-
const result = await coreTaskDepends(projectRoot, taskId, direction);
|
|
11229
|
+
const result = await coreTaskDepends(projectRoot, taskId, direction, tree ? { tree } : void 0);
|
|
10631
11230
|
return { success: true, data: result };
|
|
10632
11231
|
} catch (err) {
|
|
10633
11232
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -10637,6 +11236,24 @@ async function taskDepends(projectRoot, taskId, direction = "both") {
|
|
|
10637
11236
|
return engineError("E_NOT_INITIALIZED", "Task database not initialized");
|
|
10638
11237
|
}
|
|
10639
11238
|
}
|
|
11239
|
+
async function taskDepsOverview(projectRoot) {
|
|
11240
|
+
try {
|
|
11241
|
+
const result = await coreTaskDepsOverview(projectRoot);
|
|
11242
|
+
return { success: true, data: result };
|
|
11243
|
+
} catch (err) {
|
|
11244
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
11245
|
+
return engineError("E_NOT_INITIALIZED", message);
|
|
11246
|
+
}
|
|
11247
|
+
}
|
|
11248
|
+
async function taskDepsCycles(projectRoot) {
|
|
11249
|
+
try {
|
|
11250
|
+
const result = await coreTaskDepsCycles(projectRoot);
|
|
11251
|
+
return { success: true, data: result };
|
|
11252
|
+
} catch (err) {
|
|
11253
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
11254
|
+
return engineError("E_NOT_INITIALIZED", message);
|
|
11255
|
+
}
|
|
11256
|
+
}
|
|
10640
11257
|
async function taskPlan(projectRoot) {
|
|
10641
11258
|
const { coreTaskPlan: coreTaskPlan2 } = await Promise.resolve().then(() => (init_plan(), plan_exports));
|
|
10642
11259
|
try {
|
|
@@ -10894,7 +11511,7 @@ init_decisions();
|
|
|
10894
11511
|
init_data_accessor();
|
|
10895
11512
|
init_errors();
|
|
10896
11513
|
init_exit_codes();
|
|
10897
|
-
import { randomBytes as
|
|
11514
|
+
import { randomBytes as randomBytes4 } from "node:crypto";
|
|
10898
11515
|
import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync5, existsSync as existsSync10 } from "node:fs";
|
|
10899
11516
|
import { join as join10 } from "node:path";
|
|
10900
11517
|
async function recordAssumption(projectRoot, params) {
|
|
@@ -10914,7 +11531,7 @@ async function recordAssumption(projectRoot, params) {
|
|
|
10914
11531
|
const taskData = await accessor.loadTaskFile();
|
|
10915
11532
|
const current = taskData;
|
|
10916
11533
|
const sessionId = params.sessionId || current._meta?.activeSession || "default";
|
|
10917
|
-
const id = `asm-${
|
|
11534
|
+
const id = `asm-${randomBytes4(8).toString("hex")}`;
|
|
10918
11535
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
10919
11536
|
const record = {
|
|
10920
11537
|
id,
|
|
@@ -10981,6 +11598,12 @@ async function computeBriefing(projectRoot, options = {}) {
|
|
|
10981
11598
|
scopeTaskIds
|
|
10982
11599
|
});
|
|
10983
11600
|
const pipelineStage = computePipelineStage(current);
|
|
11601
|
+
const warnings = [];
|
|
11602
|
+
if (currentTaskInfo?.blockedBy?.length) {
|
|
11603
|
+
warnings.push(
|
|
11604
|
+
`Focused task ${currentTaskInfo.id} is blocked by: ${currentTaskInfo.blockedBy.join(", ")}`
|
|
11605
|
+
);
|
|
11606
|
+
}
|
|
10984
11607
|
return {
|
|
10985
11608
|
lastSession,
|
|
10986
11609
|
currentTask: currentTaskInfo,
|
|
@@ -10988,7 +11611,8 @@ async function computeBriefing(projectRoot, options = {}) {
|
|
|
10988
11611
|
openBugs,
|
|
10989
11612
|
blockedTasks,
|
|
10990
11613
|
activeEpics,
|
|
10991
|
-
...pipelineStage && { pipelineStage }
|
|
11614
|
+
...pipelineStage && { pipelineStage },
|
|
11615
|
+
...warnings.length > 0 && { warnings }
|
|
10992
11616
|
};
|
|
10993
11617
|
}
|
|
10994
11618
|
function parseScope(scopeStr, current) {
|
|
@@ -11076,11 +11700,21 @@ function computeCurrentTask(current, taskMap) {
|
|
|
11076
11700
|
if (!focusTaskId) return null;
|
|
11077
11701
|
const task = taskMap.get(focusTaskId);
|
|
11078
11702
|
if (!task) return null;
|
|
11079
|
-
|
|
11703
|
+
const info = {
|
|
11080
11704
|
id: task.id,
|
|
11081
11705
|
title: task.title,
|
|
11082
11706
|
status: task.status
|
|
11083
11707
|
};
|
|
11708
|
+
if (task.depends?.length) {
|
|
11709
|
+
const unresolved = task.depends.filter((depId) => {
|
|
11710
|
+
const dep = taskMap.get(depId);
|
|
11711
|
+
return dep && dep.status !== "done" && dep.status !== "cancelled";
|
|
11712
|
+
});
|
|
11713
|
+
if (unresolved.length > 0) {
|
|
11714
|
+
info.blockedBy = unresolved;
|
|
11715
|
+
}
|
|
11716
|
+
}
|
|
11717
|
+
return info;
|
|
11084
11718
|
}
|
|
11085
11719
|
function calculateLeverage2(taskId, taskMap) {
|
|
11086
11720
|
let leverage = 0;
|
|
@@ -11227,11 +11861,11 @@ function computePipelineStage(current) {
|
|
|
11227
11861
|
}
|
|
11228
11862
|
|
|
11229
11863
|
// src/core/sessions/session-id.ts
|
|
11230
|
-
import { randomBytes as
|
|
11864
|
+
import { randomBytes as randomBytes5 } from "node:crypto";
|
|
11231
11865
|
function generateSessionId() {
|
|
11232
11866
|
const now = /* @__PURE__ */ new Date();
|
|
11233
11867
|
const ts = now.toISOString().replace(/[-:T]/g, "").substring(0, 14);
|
|
11234
|
-
const hex =
|
|
11868
|
+
const hex = randomBytes5(3).toString("hex");
|
|
11235
11869
|
return `ses_${ts}_${hex}`;
|
|
11236
11870
|
}
|
|
11237
11871
|
|
|
@@ -11269,6 +11903,14 @@ async function startTask(taskId, cwd, accessor) {
|
|
|
11269
11903
|
{ fix: `Use 'cleo find "${taskId}"' to search` }
|
|
11270
11904
|
);
|
|
11271
11905
|
}
|
|
11906
|
+
const unresolvedDeps = getUnresolvedDeps(taskId, data.tasks);
|
|
11907
|
+
if (unresolvedDeps.length > 0) {
|
|
11908
|
+
throw new CleoError(
|
|
11909
|
+
5 /* DEPENDENCY_ERROR */,
|
|
11910
|
+
`Task ${taskId} is blocked by unresolved dependencies: ${unresolvedDeps.join(", ")}`,
|
|
11911
|
+
{ fix: `Complete blockers first: ${unresolvedDeps.map((d) => `cleo complete ${d}`).join(", ")}` }
|
|
11912
|
+
);
|
|
11913
|
+
}
|
|
11272
11914
|
const previousTask = data.focus?.currentTask ?? null;
|
|
11273
11915
|
if (!data.focus) {
|
|
11274
11916
|
data.focus = {};
|
|
@@ -11327,6 +11969,8 @@ async function stopTask(cwd, accessor) {
|
|
|
11327
11969
|
}
|
|
11328
11970
|
|
|
11329
11971
|
// src/dispatch/engines/session-engine.ts
|
|
11972
|
+
init_errors();
|
|
11973
|
+
init_exit_codes();
|
|
11330
11974
|
async function sessionStatus(projectRoot) {
|
|
11331
11975
|
try {
|
|
11332
11976
|
const accessor = await getAccessor(projectRoot);
|
|
@@ -11396,6 +12040,12 @@ async function taskStart(projectRoot, taskId) {
|
|
|
11396
12040
|
data: { taskId: result.taskId, previousTask: result.previousTask }
|
|
11397
12041
|
};
|
|
11398
12042
|
} catch (err) {
|
|
12043
|
+
if (err instanceof CleoError) {
|
|
12044
|
+
const stringCode = `E_${getExitCodeName(err.code)}`;
|
|
12045
|
+
return engineError(stringCode, err.message, {
|
|
12046
|
+
...err.fix && { fix: err.fix }
|
|
12047
|
+
});
|
|
12048
|
+
}
|
|
11399
12049
|
const message = err.message;
|
|
11400
12050
|
const code = message.includes("not found") ? "E_NOT_FOUND" : "E_NOT_INITIALIZED";
|
|
11401
12051
|
return engineError(code, message);
|
|
@@ -11860,8 +12510,8 @@ async function sessionChainShow(projectRoot, sessionId) {
|
|
|
11860
12510
|
// src/dispatch/engines/system-engine.ts
|
|
11861
12511
|
init_platform();
|
|
11862
12512
|
init_data_accessor();
|
|
11863
|
-
import { readFileSync as
|
|
11864
|
-
import { join as
|
|
12513
|
+
import { readFileSync as readFileSync13, existsSync as existsSync20, readdirSync as readdirSync5 } from "node:fs";
|
|
12514
|
+
import { join as join20, basename as basename4 } from "node:path";
|
|
11865
12515
|
|
|
11866
12516
|
// src/core/stats/index.ts
|
|
11867
12517
|
init_json();
|
|
@@ -11989,14 +12639,14 @@ init_paths();
|
|
|
11989
12639
|
// src/core/system/inject-generate.ts
|
|
11990
12640
|
init_json();
|
|
11991
12641
|
init_paths();
|
|
11992
|
-
import { readFileSync as
|
|
11993
|
-
import { join as
|
|
12642
|
+
import { readFileSync as readFileSync6, existsSync as existsSync12 } from "node:fs";
|
|
12643
|
+
import { join as join12 } from "node:path";
|
|
11994
12644
|
async function generateInjection(projectRoot, accessor) {
|
|
11995
12645
|
let version = "unknown";
|
|
11996
12646
|
try {
|
|
11997
|
-
const pkgPath =
|
|
11998
|
-
if (
|
|
11999
|
-
const pkg = JSON.parse(
|
|
12647
|
+
const pkgPath = join12(projectRoot, "package.json");
|
|
12648
|
+
if (existsSync12(pkgPath)) {
|
|
12649
|
+
const pkg = JSON.parse(readFileSync6(pkgPath, "utf-8"));
|
|
12000
12650
|
version = pkg.version || "unknown";
|
|
12001
12651
|
}
|
|
12002
12652
|
} catch {
|
|
@@ -12011,8 +12661,8 @@ async function generateInjection(projectRoot, accessor) {
|
|
|
12011
12661
|
}
|
|
12012
12662
|
try {
|
|
12013
12663
|
const sessionsPath = getSessionsPath(projectRoot);
|
|
12014
|
-
if (
|
|
12015
|
-
const sessionsData = JSON.parse(
|
|
12664
|
+
if (existsSync12(sessionsPath)) {
|
|
12665
|
+
const sessionsData = JSON.parse(readFileSync6(sessionsPath, "utf-8"));
|
|
12016
12666
|
const active = sessionsData.sessions?.find((s) => s.status === "active");
|
|
12017
12667
|
if (active) {
|
|
12018
12668
|
activeSessionName = active.name || active.id;
|
|
@@ -12093,8 +12743,8 @@ For full protocol details, load the **ct-cleo** skill: \`cleo_query({ domain: "s
|
|
|
12093
12743
|
}
|
|
12094
12744
|
|
|
12095
12745
|
// src/core/system/health.ts
|
|
12096
|
-
import { existsSync as
|
|
12097
|
-
import { join as
|
|
12746
|
+
import { existsSync as existsSync14, readFileSync as readFileSync8, statSync as statSync4 } from "node:fs";
|
|
12747
|
+
import { join as join14 } from "node:path";
|
|
12098
12748
|
import { execFile as execFile3 } from "node:child_process";
|
|
12099
12749
|
import { promisify as promisify3 } from "node:util";
|
|
12100
12750
|
|
|
@@ -12106,25 +12756,25 @@ init_platform();
|
|
|
12106
12756
|
|
|
12107
12757
|
// src/core/migration/agent-outputs.ts
|
|
12108
12758
|
import {
|
|
12109
|
-
existsSync as
|
|
12110
|
-
readFileSync as
|
|
12111
|
-
writeFileSync as
|
|
12112
|
-
mkdirSync as
|
|
12759
|
+
existsSync as existsSync13,
|
|
12760
|
+
readFileSync as readFileSync7,
|
|
12761
|
+
writeFileSync as writeFileSync2,
|
|
12762
|
+
mkdirSync as mkdirSync6,
|
|
12113
12763
|
readdirSync as readdirSync3,
|
|
12114
12764
|
copyFileSync,
|
|
12115
12765
|
statSync as statSync3,
|
|
12116
12766
|
rmSync
|
|
12117
12767
|
} from "node:fs";
|
|
12118
|
-
import { join as
|
|
12768
|
+
import { join as join13 } from "node:path";
|
|
12119
12769
|
var CANONICAL_DIR = ".cleo/agent-outputs";
|
|
12120
12770
|
var MANIFEST_PATH_REWRITES = [
|
|
12121
12771
|
[/claudedocs\/research-outputs\//g, `${CANONICAL_DIR}/`],
|
|
12122
12772
|
[/claudedocs\/agent-outputs\//g, `${CANONICAL_DIR}/`]
|
|
12123
12773
|
];
|
|
12124
12774
|
function detectLegacyAgentOutputs(projectRoot, cleoDir) {
|
|
12125
|
-
const hasResearchOutputs =
|
|
12126
|
-
const hasLegacyAgentOutputs =
|
|
12127
|
-
const hasCanonical =
|
|
12775
|
+
const hasResearchOutputs = existsSync13(join13(projectRoot, "claudedocs", "research-outputs"));
|
|
12776
|
+
const hasLegacyAgentOutputs = existsSync13(join13(projectRoot, "claudedocs", "agent-outputs"));
|
|
12777
|
+
const hasCanonical = existsSync13(join13(cleoDir, "agent-outputs"));
|
|
12128
12778
|
const legacyPaths = [];
|
|
12129
12779
|
if (hasResearchOutputs) legacyPaths.push("claudedocs/research-outputs/");
|
|
12130
12780
|
if (hasLegacyAgentOutputs) legacyPaths.push("claudedocs/agent-outputs/");
|
|
@@ -12147,30 +12797,30 @@ function migrateAgentOutputs(projectRoot, cleoDir) {
|
|
|
12147
12797
|
summary: "No legacy output directories found"
|
|
12148
12798
|
};
|
|
12149
12799
|
}
|
|
12150
|
-
const newDir =
|
|
12800
|
+
const newDir = join13(cleoDir, "agent-outputs");
|
|
12151
12801
|
const hadCanonical = detection.hasCanonical;
|
|
12152
|
-
|
|
12802
|
+
mkdirSync6(newDir, { recursive: true });
|
|
12153
12803
|
let totalCopied = 0;
|
|
12154
12804
|
const mergedManifestLines = [];
|
|
12155
12805
|
const copiedFiles = /* @__PURE__ */ new Set();
|
|
12156
12806
|
const legacySources = [
|
|
12157
|
-
{ path:
|
|
12158
|
-
{ path:
|
|
12807
|
+
{ path: join13(projectRoot, "claudedocs", "research-outputs"), exists: detection.hasResearchOutputs },
|
|
12808
|
+
{ path: join13(projectRoot, "claudedocs", "agent-outputs"), exists: detection.hasLegacyAgentOutputs }
|
|
12159
12809
|
];
|
|
12160
12810
|
for (const source of legacySources) {
|
|
12161
12811
|
if (!source.exists) continue;
|
|
12162
12812
|
totalCopied += copyDirContents(source.path, newDir, mergedManifestLines, copiedFiles);
|
|
12163
12813
|
}
|
|
12164
|
-
const
|
|
12814
|
+
const manifestEntries2 = mergeManifests(newDir, hadCanonical, mergedManifestLines);
|
|
12165
12815
|
updateConfigPaths(cleoDir);
|
|
12166
12816
|
const removed = removeLegacyDirs(projectRoot, detection);
|
|
12167
12817
|
const parts = [`Migrated ${totalCopied} files \u2192 ${CANONICAL_DIR}/`];
|
|
12168
|
-
if (
|
|
12818
|
+
if (manifestEntries2 > 0) parts.push(`merged ${manifestEntries2} manifest entries`);
|
|
12169
12819
|
if (removed.length > 0) parts.push(`removed: ${removed.join(", ")}`);
|
|
12170
12820
|
return {
|
|
12171
12821
|
migrated: true,
|
|
12172
12822
|
filesCopied: totalCopied,
|
|
12173
|
-
manifestEntries,
|
|
12823
|
+
manifestEntries: manifestEntries2,
|
|
12174
12824
|
removed,
|
|
12175
12825
|
summary: parts.join("; ")
|
|
12176
12826
|
};
|
|
@@ -12180,19 +12830,19 @@ function copyDirContents(srcDir, dstDir, manifestLines, copiedFiles) {
|
|
|
12180
12830
|
const entries = readdirSync3(srcDir);
|
|
12181
12831
|
for (const entry of entries) {
|
|
12182
12832
|
if (entry === "MANIFEST.jsonl") {
|
|
12183
|
-
collectManifestLines(
|
|
12833
|
+
collectManifestLines(join13(srcDir, entry), manifestLines);
|
|
12184
12834
|
continue;
|
|
12185
12835
|
}
|
|
12186
|
-
const srcPath =
|
|
12187
|
-
const dstPath =
|
|
12836
|
+
const srcPath = join13(srcDir, entry);
|
|
12837
|
+
const dstPath = join13(dstDir, entry);
|
|
12188
12838
|
try {
|
|
12189
12839
|
const st = statSync3(srcPath);
|
|
12190
12840
|
if (st.isDirectory()) {
|
|
12191
|
-
|
|
12841
|
+
mkdirSync6(dstPath, { recursive: true });
|
|
12192
12842
|
for (const sf of readdirSync3(srcPath)) {
|
|
12193
12843
|
if (!copiedFiles.has(sf)) {
|
|
12194
12844
|
try {
|
|
12195
|
-
copyFileSync(
|
|
12845
|
+
copyFileSync(join13(srcPath, sf), join13(dstPath, sf));
|
|
12196
12846
|
copiedFiles.add(sf);
|
|
12197
12847
|
count2++;
|
|
12198
12848
|
} catch {
|
|
@@ -12211,7 +12861,7 @@ function copyDirContents(srcDir, dstDir, manifestLines, copiedFiles) {
|
|
|
12211
12861
|
}
|
|
12212
12862
|
function collectManifestLines(manifestPath, out) {
|
|
12213
12863
|
try {
|
|
12214
|
-
const content =
|
|
12864
|
+
const content = readFileSync7(manifestPath, "utf-8");
|
|
12215
12865
|
for (const line of content.split("\n")) {
|
|
12216
12866
|
if (!line.trim()) continue;
|
|
12217
12867
|
let rewritten = line;
|
|
@@ -12224,11 +12874,11 @@ function collectManifestLines(manifestPath, out) {
|
|
|
12224
12874
|
}
|
|
12225
12875
|
}
|
|
12226
12876
|
function mergeManifests(newDir, hadCanonical, legacyLines) {
|
|
12227
|
-
const manifestPath =
|
|
12877
|
+
const manifestPath = join13(newDir, "MANIFEST.jsonl");
|
|
12228
12878
|
const existingLines = [];
|
|
12229
|
-
if (hadCanonical &&
|
|
12879
|
+
if (hadCanonical && existsSync13(manifestPath)) {
|
|
12230
12880
|
try {
|
|
12231
|
-
const existing =
|
|
12881
|
+
const existing = readFileSync7(manifestPath, "utf-8");
|
|
12232
12882
|
for (const line of existing.split("\n")) {
|
|
12233
12883
|
if (line.trim()) existingLines.push(line);
|
|
12234
12884
|
}
|
|
@@ -12255,15 +12905,15 @@ function mergeManifests(newDir, hadCanonical, legacyLines) {
|
|
|
12255
12905
|
finalLines.push(line);
|
|
12256
12906
|
}
|
|
12257
12907
|
if (finalLines.length > 0) {
|
|
12258
|
-
|
|
12908
|
+
writeFileSync2(manifestPath, finalLines.join("\n") + "\n");
|
|
12259
12909
|
}
|
|
12260
12910
|
return finalLines.length;
|
|
12261
12911
|
}
|
|
12262
12912
|
function updateConfigPaths(cleoDir) {
|
|
12263
|
-
const configPath =
|
|
12264
|
-
if (!
|
|
12913
|
+
const configPath = join13(cleoDir, "config.json");
|
|
12914
|
+
if (!existsSync13(configPath)) return;
|
|
12265
12915
|
try {
|
|
12266
|
-
const config = JSON.parse(
|
|
12916
|
+
const config = JSON.parse(readFileSync7(configPath, "utf-8"));
|
|
12267
12917
|
const currentDir = config.agentOutputs?.directory ?? config.agentOutputs ?? config.research?.outputDir;
|
|
12268
12918
|
if (currentDir && currentDir !== CANONICAL_DIR) {
|
|
12269
12919
|
if (typeof config.agentOutputs === "object") {
|
|
@@ -12277,7 +12927,7 @@ function updateConfigPaths(cleoDir) {
|
|
|
12277
12927
|
delete config.research;
|
|
12278
12928
|
}
|
|
12279
12929
|
}
|
|
12280
|
-
|
|
12930
|
+
writeFileSync2(configPath, JSON.stringify(config, null, 2));
|
|
12281
12931
|
}
|
|
12282
12932
|
} catch {
|
|
12283
12933
|
}
|
|
@@ -12286,20 +12936,20 @@ function removeLegacyDirs(projectRoot, detection) {
|
|
|
12286
12936
|
const removed = [];
|
|
12287
12937
|
if (detection.hasResearchOutputs) {
|
|
12288
12938
|
try {
|
|
12289
|
-
rmSync(
|
|
12939
|
+
rmSync(join13(projectRoot, "claudedocs", "research-outputs"), { recursive: true, force: true });
|
|
12290
12940
|
removed.push("claudedocs/research-outputs/");
|
|
12291
12941
|
} catch {
|
|
12292
12942
|
}
|
|
12293
12943
|
}
|
|
12294
12944
|
if (detection.hasLegacyAgentOutputs) {
|
|
12295
12945
|
try {
|
|
12296
|
-
rmSync(
|
|
12946
|
+
rmSync(join13(projectRoot, "claudedocs", "agent-outputs"), { recursive: true, force: true });
|
|
12297
12947
|
removed.push("claudedocs/agent-outputs/");
|
|
12298
12948
|
} catch {
|
|
12299
12949
|
}
|
|
12300
12950
|
}
|
|
12301
|
-
const claudedocsDir =
|
|
12302
|
-
if (
|
|
12951
|
+
const claudedocsDir = join13(projectRoot, "claudedocs");
|
|
12952
|
+
if (existsSync13(claudedocsDir)) {
|
|
12303
12953
|
try {
|
|
12304
12954
|
if (readdirSync3(claudedocsDir).length === 0) {
|
|
12305
12955
|
rmSync(claudedocsDir, { recursive: true, force: true });
|
|
@@ -12316,15 +12966,15 @@ init_data_accessor();
|
|
|
12316
12966
|
var execAsync = promisify3(execFile3);
|
|
12317
12967
|
var STALE_JSON_FILES = ["todo.json", "sessions.json", "todo-archive.json"];
|
|
12318
12968
|
function getSystemHealth(projectRoot, opts) {
|
|
12319
|
-
const cleoDir =
|
|
12969
|
+
const cleoDir = join14(projectRoot, ".cleo");
|
|
12320
12970
|
const checks = [];
|
|
12321
|
-
if (
|
|
12971
|
+
if (existsSync14(cleoDir)) {
|
|
12322
12972
|
checks.push({ name: "cleo_dir", status: "pass", message: ".cleo directory exists" });
|
|
12323
12973
|
} else {
|
|
12324
12974
|
checks.push({ name: "cleo_dir", status: "fail", message: ".cleo directory not found" });
|
|
12325
12975
|
}
|
|
12326
|
-
const dbPath =
|
|
12327
|
-
if (
|
|
12976
|
+
const dbPath = join14(cleoDir, "tasks.db");
|
|
12977
|
+
if (existsSync14(dbPath)) {
|
|
12328
12978
|
try {
|
|
12329
12979
|
const dbSize = statSync4(dbPath).size;
|
|
12330
12980
|
if (dbSize > 0) {
|
|
@@ -12338,10 +12988,10 @@ function getSystemHealth(projectRoot, opts) {
|
|
|
12338
12988
|
} else {
|
|
12339
12989
|
checks.push({ name: "tasks_db", status: "fail", message: "tasks.db not found" });
|
|
12340
12990
|
}
|
|
12341
|
-
const configPath =
|
|
12342
|
-
if (
|
|
12991
|
+
const configPath = join14(cleoDir, "config.json");
|
|
12992
|
+
if (existsSync14(configPath)) {
|
|
12343
12993
|
try {
|
|
12344
|
-
JSON.parse(
|
|
12994
|
+
JSON.parse(readFileSync8(configPath, "utf-8"));
|
|
12345
12995
|
checks.push({ name: "config_json", status: "pass", message: "config.json is valid JSON" });
|
|
12346
12996
|
} catch {
|
|
12347
12997
|
checks.push({ name: "config_json", status: "warn", message: "config.json is not valid JSON" });
|
|
@@ -12349,8 +12999,8 @@ function getSystemHealth(projectRoot, opts) {
|
|
|
12349
12999
|
} else {
|
|
12350
13000
|
checks.push({ name: "config_json", status: "warn", message: "config.json not found" });
|
|
12351
13001
|
}
|
|
12352
|
-
if (
|
|
12353
|
-
const staleFiles = STALE_JSON_FILES.filter((f) =>
|
|
13002
|
+
if (existsSync14(dbPath)) {
|
|
13003
|
+
const staleFiles = STALE_JSON_FILES.filter((f) => existsSync14(join14(cleoDir, f)));
|
|
12354
13004
|
if (staleFiles.length > 0) {
|
|
12355
13005
|
checks.push({
|
|
12356
13006
|
name: "stale_json",
|
|
@@ -12360,14 +13010,14 @@ function getSystemHealth(projectRoot, opts) {
|
|
|
12360
13010
|
}
|
|
12361
13011
|
}
|
|
12362
13012
|
if (opts?.detailed) {
|
|
12363
|
-
const logPath =
|
|
12364
|
-
if (
|
|
13013
|
+
const logPath = join14(cleoDir, "todo-log.jsonl");
|
|
13014
|
+
if (existsSync14(logPath)) {
|
|
12365
13015
|
checks.push({ name: "log_file", status: "pass", message: "todo-log.jsonl exists" });
|
|
12366
13016
|
} else {
|
|
12367
13017
|
checks.push({ name: "log_file", status: "warn", message: "todo-log.jsonl not found" });
|
|
12368
13018
|
}
|
|
12369
|
-
const backupDir =
|
|
12370
|
-
if (
|
|
13019
|
+
const backupDir = join14(cleoDir, ".backups");
|
|
13020
|
+
if (existsSync14(backupDir)) {
|
|
12371
13021
|
checks.push({ name: "backups_dir", status: "pass", message: ".backups directory exists" });
|
|
12372
13022
|
} else {
|
|
12373
13023
|
checks.push({ name: "backups_dir", status: "pass", message: "No backups directory (created on first write)" });
|
|
@@ -12375,9 +13025,9 @@ function getSystemHealth(projectRoot, opts) {
|
|
|
12375
13025
|
}
|
|
12376
13026
|
let version = "unknown";
|
|
12377
13027
|
try {
|
|
12378
|
-
const pkgPath =
|
|
12379
|
-
if (
|
|
12380
|
-
const pkg = JSON.parse(
|
|
13028
|
+
const pkgPath = join14(projectRoot, "package.json");
|
|
13029
|
+
if (existsSync14(pkgPath)) {
|
|
13030
|
+
const pkg = JSON.parse(readFileSync8(pkgPath, "utf-8"));
|
|
12381
13031
|
version = pkg.version || "unknown";
|
|
12382
13032
|
}
|
|
12383
13033
|
} catch {
|
|
@@ -12396,34 +13046,34 @@ init_paths();
|
|
|
12396
13046
|
// src/core/system/backup.ts
|
|
12397
13047
|
init_errors();
|
|
12398
13048
|
init_exit_codes();
|
|
12399
|
-
import { readFileSync as
|
|
12400
|
-
import { join as
|
|
13049
|
+
import { readFileSync as readFileSync9, writeFileSync as writeFileSync3, existsSync as existsSync15, mkdirSync as mkdirSync7 } from "node:fs";
|
|
13050
|
+
import { join as join15 } from "node:path";
|
|
12401
13051
|
function createBackup2(projectRoot, opts) {
|
|
12402
|
-
const cleoDir =
|
|
13052
|
+
const cleoDir = join15(projectRoot, ".cleo");
|
|
12403
13053
|
const btype = opts?.type || "snapshot";
|
|
12404
13054
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
12405
13055
|
const backupId = `${btype}-${timestamp.replace(/[:.]/g, "-")}`;
|
|
12406
|
-
const backupDir =
|
|
12407
|
-
if (!
|
|
12408
|
-
|
|
13056
|
+
const backupDir = join15(cleoDir, "backups", btype);
|
|
13057
|
+
if (!existsSync15(backupDir)) {
|
|
13058
|
+
mkdirSync7(backupDir, { recursive: true });
|
|
12409
13059
|
}
|
|
12410
13060
|
const filesToBackup = ["todo.json", "todo-archive.json", "sessions.json", "config.json", "todo-log.jsonl"];
|
|
12411
13061
|
const backedUp = [];
|
|
12412
13062
|
for (const file of filesToBackup) {
|
|
12413
|
-
const src =
|
|
12414
|
-
if (
|
|
12415
|
-
const dest =
|
|
13063
|
+
const src = join15(cleoDir, file);
|
|
13064
|
+
if (existsSync15(src)) {
|
|
13065
|
+
const dest = join15(backupDir, `${file}.${backupId}`);
|
|
12416
13066
|
try {
|
|
12417
|
-
const content =
|
|
12418
|
-
|
|
13067
|
+
const content = readFileSync9(src, "utf-8");
|
|
13068
|
+
writeFileSync3(dest, content, "utf-8");
|
|
12419
13069
|
backedUp.push(file);
|
|
12420
13070
|
} catch {
|
|
12421
13071
|
}
|
|
12422
13072
|
}
|
|
12423
13073
|
}
|
|
12424
|
-
const metaPath =
|
|
13074
|
+
const metaPath = join15(backupDir, `${backupId}.meta.json`);
|
|
12425
13075
|
try {
|
|
12426
|
-
|
|
13076
|
+
writeFileSync3(metaPath, JSON.stringify({
|
|
12427
13077
|
backupId,
|
|
12428
13078
|
type: btype,
|
|
12429
13079
|
timestamp,
|
|
@@ -12438,15 +13088,15 @@ function restoreBackup(projectRoot, params) {
|
|
|
12438
13088
|
if (!params.backupId) {
|
|
12439
13089
|
throw new CleoError(2 /* INVALID_INPUT */, "backupId is required");
|
|
12440
13090
|
}
|
|
12441
|
-
const cleoDir =
|
|
13091
|
+
const cleoDir = join15(projectRoot, ".cleo");
|
|
12442
13092
|
const backupTypes = ["snapshot", "safety", "migration"];
|
|
12443
13093
|
let metaPath = null;
|
|
12444
13094
|
let backupDir = null;
|
|
12445
13095
|
for (const btype of backupTypes) {
|
|
12446
|
-
const candidateMeta =
|
|
12447
|
-
if (
|
|
13096
|
+
const candidateMeta = join15(cleoDir, "backups", btype, `${params.backupId}.meta.json`);
|
|
13097
|
+
if (existsSync15(candidateMeta)) {
|
|
12448
13098
|
metaPath = candidateMeta;
|
|
12449
|
-
backupDir =
|
|
13099
|
+
backupDir = join15(cleoDir, "backups", btype);
|
|
12450
13100
|
break;
|
|
12451
13101
|
}
|
|
12452
13102
|
}
|
|
@@ -12455,17 +13105,17 @@ function restoreBackup(projectRoot, params) {
|
|
|
12455
13105
|
}
|
|
12456
13106
|
let meta;
|
|
12457
13107
|
try {
|
|
12458
|
-
meta = JSON.parse(
|
|
13108
|
+
meta = JSON.parse(readFileSync9(metaPath, "utf-8"));
|
|
12459
13109
|
} catch {
|
|
12460
13110
|
throw new CleoError(3 /* FILE_ERROR */, "Failed to read backup metadata");
|
|
12461
13111
|
}
|
|
12462
13112
|
const restored = [];
|
|
12463
13113
|
for (const file of meta.files ?? []) {
|
|
12464
|
-
const backupFile =
|
|
12465
|
-
if (
|
|
13114
|
+
const backupFile = join15(backupDir, `${file}.${params.backupId}`);
|
|
13115
|
+
if (existsSync15(backupFile)) {
|
|
12466
13116
|
try {
|
|
12467
|
-
const content =
|
|
12468
|
-
|
|
13117
|
+
const content = readFileSync9(backupFile, "utf-8");
|
|
13118
|
+
writeFileSync3(join15(cleoDir, file), content, "utf-8");
|
|
12469
13119
|
restored.push(file);
|
|
12470
13120
|
} catch {
|
|
12471
13121
|
}
|
|
@@ -12482,14 +13132,14 @@ function restoreBackup(projectRoot, params) {
|
|
|
12482
13132
|
// src/core/system/migrate.ts
|
|
12483
13133
|
init_errors();
|
|
12484
13134
|
init_exit_codes();
|
|
12485
|
-
import { readFileSync as
|
|
12486
|
-
import { join as
|
|
13135
|
+
import { readFileSync as readFileSync10, existsSync as existsSync16 } from "node:fs";
|
|
13136
|
+
import { join as join16 } from "node:path";
|
|
12487
13137
|
function getMigrationStatus(projectRoot, opts) {
|
|
12488
|
-
const taskPath =
|
|
13138
|
+
const taskPath = join16(projectRoot, ".cleo", "tasks.json");
|
|
12489
13139
|
let currentVersion = "unknown";
|
|
12490
|
-
if (
|
|
13140
|
+
if (existsSync16(taskPath)) {
|
|
12491
13141
|
try {
|
|
12492
|
-
const taskFile = JSON.parse(
|
|
13142
|
+
const taskFile = JSON.parse(readFileSync10(taskPath, "utf-8"));
|
|
12493
13143
|
currentVersion = taskFile._meta?.schemaVersion ?? taskFile.version ?? "unknown";
|
|
12494
13144
|
} catch {
|
|
12495
13145
|
throw new CleoError(3 /* FILE_ERROR */, "Failed to read tasks.json");
|
|
@@ -12509,21 +13159,21 @@ function getMigrationStatus(projectRoot, opts) {
|
|
|
12509
13159
|
// src/core/system/cleanup.ts
|
|
12510
13160
|
init_errors();
|
|
12511
13161
|
init_exit_codes();
|
|
12512
|
-
import { readFileSync as
|
|
12513
|
-
import { join as
|
|
13162
|
+
import { readFileSync as readFileSync11, writeFileSync as writeFileSync4, existsSync as existsSync17, readdirSync as readdirSync4, unlinkSync as unlinkSync3 } from "node:fs";
|
|
13163
|
+
import { join as join17 } from "node:path";
|
|
12514
13164
|
function cleanupSystem(projectRoot, params) {
|
|
12515
13165
|
if (!params.target) {
|
|
12516
13166
|
throw new CleoError(2 /* INVALID_INPUT */, "target is required (sessions|backups|logs|archive)");
|
|
12517
13167
|
}
|
|
12518
|
-
const cleoDir =
|
|
13168
|
+
const cleoDir = join17(projectRoot, ".cleo");
|
|
12519
13169
|
const dryRun = params.dryRun ?? false;
|
|
12520
13170
|
const items = [];
|
|
12521
13171
|
switch (params.target) {
|
|
12522
13172
|
case "sessions": {
|
|
12523
|
-
const sessPath =
|
|
12524
|
-
if (
|
|
13173
|
+
const sessPath = join17(cleoDir, "sessions.json");
|
|
13174
|
+
if (existsSync17(sessPath)) {
|
|
12525
13175
|
try {
|
|
12526
|
-
const data = JSON.parse(
|
|
13176
|
+
const data = JSON.parse(readFileSync11(sessPath, "utf-8"));
|
|
12527
13177
|
const sessions2 = data.sessions ?? [];
|
|
12528
13178
|
const stale = sessions2.filter((s) => {
|
|
12529
13179
|
if (s.status !== "active") return false;
|
|
@@ -12539,7 +13189,7 @@ function cleanupSystem(projectRoot, params) {
|
|
|
12539
13189
|
if (!dryRun && stale.length > 0) {
|
|
12540
13190
|
const staleIds = new Set(stale.map((s) => s.id));
|
|
12541
13191
|
data.sessions = sessions2.filter((s) => !staleIds.has(s.id));
|
|
12542
|
-
|
|
13192
|
+
writeFileSync4(sessPath, JSON.stringify(data, null, 2), "utf-8");
|
|
12543
13193
|
}
|
|
12544
13194
|
} catch {
|
|
12545
13195
|
}
|
|
@@ -12547,24 +13197,24 @@ function cleanupSystem(projectRoot, params) {
|
|
|
12547
13197
|
break;
|
|
12548
13198
|
}
|
|
12549
13199
|
case "backups": {
|
|
12550
|
-
const backupBaseDir =
|
|
12551
|
-
if (
|
|
13200
|
+
const backupBaseDir = join17(cleoDir, "backups");
|
|
13201
|
+
if (existsSync17(backupBaseDir)) {
|
|
12552
13202
|
for (const typeDir of readdirSync4(backupBaseDir)) {
|
|
12553
|
-
const fullDir =
|
|
13203
|
+
const fullDir = join17(backupBaseDir, typeDir);
|
|
12554
13204
|
try {
|
|
12555
13205
|
for (const file of readdirSync4(fullDir)) {
|
|
12556
13206
|
if (file.endsWith(".meta.json")) {
|
|
12557
|
-
const metaFilePath =
|
|
13207
|
+
const metaFilePath = join17(fullDir, file);
|
|
12558
13208
|
try {
|
|
12559
|
-
const meta = JSON.parse(
|
|
13209
|
+
const meta = JSON.parse(readFileSync11(metaFilePath, "utf-8"));
|
|
12560
13210
|
if (params.olderThan && meta.timestamp < params.olderThan) {
|
|
12561
13211
|
items.push(file.replace(".meta.json", ""));
|
|
12562
13212
|
if (!dryRun) {
|
|
12563
|
-
|
|
13213
|
+
unlinkSync3(metaFilePath);
|
|
12564
13214
|
for (const bf of readdirSync4(fullDir)) {
|
|
12565
13215
|
if (bf.includes(meta.backupId)) {
|
|
12566
13216
|
try {
|
|
12567
|
-
|
|
13217
|
+
unlinkSync3(join17(fullDir, bf));
|
|
12568
13218
|
} catch {
|
|
12569
13219
|
}
|
|
12570
13220
|
}
|
|
@@ -12583,13 +13233,13 @@ function cleanupSystem(projectRoot, params) {
|
|
|
12583
13233
|
}
|
|
12584
13234
|
case "logs": {
|
|
12585
13235
|
const auditPattern = /^audit-log-.*\.json$/;
|
|
12586
|
-
if (
|
|
13236
|
+
if (existsSync17(cleoDir)) {
|
|
12587
13237
|
for (const file of readdirSync4(cleoDir)) {
|
|
12588
13238
|
if (auditPattern.test(file)) {
|
|
12589
13239
|
items.push(file);
|
|
12590
13240
|
if (!dryRun) {
|
|
12591
13241
|
try {
|
|
12592
|
-
|
|
13242
|
+
unlinkSync3(join17(cleoDir, file));
|
|
12593
13243
|
} catch {
|
|
12594
13244
|
}
|
|
12595
13245
|
}
|
|
@@ -12607,17 +13257,17 @@ function cleanupSystem(projectRoot, params) {
|
|
|
12607
13257
|
// src/core/system/safestop.ts
|
|
12608
13258
|
init_errors();
|
|
12609
13259
|
init_exit_codes();
|
|
12610
|
-
import { readFileSync as
|
|
12611
|
-
import { join as
|
|
13260
|
+
import { readFileSync as readFileSync12, writeFileSync as writeFileSync5, existsSync as existsSync18 } from "node:fs";
|
|
13261
|
+
import { join as join18 } from "node:path";
|
|
12612
13262
|
function safestop(projectRoot, opts) {
|
|
12613
13263
|
const dryRun = opts?.dryRun ?? false;
|
|
12614
13264
|
const reason = opts?.reason ?? "Manual safestop";
|
|
12615
13265
|
let sessionEnded = false;
|
|
12616
13266
|
if (!dryRun && !opts?.noSessionEnd) {
|
|
12617
|
-
const sessPath =
|
|
12618
|
-
if (
|
|
13267
|
+
const sessPath = join18(projectRoot, ".cleo", "sessions.json");
|
|
13268
|
+
if (existsSync18(sessPath)) {
|
|
12619
13269
|
try {
|
|
12620
|
-
const data = JSON.parse(
|
|
13270
|
+
const data = JSON.parse(readFileSync12(sessPath, "utf-8"));
|
|
12621
13271
|
const sessions2 = data.sessions ?? [];
|
|
12622
13272
|
let changed = false;
|
|
12623
13273
|
for (const s of sessions2) {
|
|
@@ -12630,7 +13280,7 @@ function safestop(projectRoot, opts) {
|
|
|
12630
13280
|
}
|
|
12631
13281
|
}
|
|
12632
13282
|
if (changed) {
|
|
12633
|
-
|
|
13283
|
+
writeFileSync5(sessPath, JSON.stringify(data, null, 2), "utf-8");
|
|
12634
13284
|
}
|
|
12635
13285
|
} catch {
|
|
12636
13286
|
}
|
|
@@ -12643,9 +13293,9 @@ function safestop(projectRoot, opts) {
|
|
|
12643
13293
|
init_sequence();
|
|
12644
13294
|
|
|
12645
13295
|
// src/core/system/runtime.ts
|
|
12646
|
-
import { existsSync as
|
|
13296
|
+
import { existsSync as existsSync19 } from "node:fs";
|
|
12647
13297
|
import { readFile as readFile3 } from "node:fs/promises";
|
|
12648
|
-
import { basename as
|
|
13298
|
+
import { basename as basename3, join as join19 } from "node:path";
|
|
12649
13299
|
import { homedir as homedir2 } from "node:os";
|
|
12650
13300
|
import { execFile as execFile4 } from "node:child_process";
|
|
12651
13301
|
import { promisify as promisify4 } from "node:util";
|
|
@@ -12679,8 +13329,8 @@ function getExpectedNaming(channel) {
|
|
|
12679
13329
|
}
|
|
12680
13330
|
}
|
|
12681
13331
|
async function parseVersionFile(dataRoot) {
|
|
12682
|
-
const versionPath =
|
|
12683
|
-
if (!
|
|
13332
|
+
const versionPath = join19(dataRoot, "VERSION");
|
|
13333
|
+
if (!existsSync19(versionPath)) return null;
|
|
12684
13334
|
let content;
|
|
12685
13335
|
try {
|
|
12686
13336
|
content = await readFile3(versionPath, "utf-8");
|
|
@@ -12709,9 +13359,9 @@ async function parseVersionFile(dataRoot) {
|
|
|
12709
13359
|
async function getPackageInfo(sourceDir) {
|
|
12710
13360
|
const candidates = [];
|
|
12711
13361
|
if (sourceDir && sourceDir !== "unknown" && sourceDir !== "npm") {
|
|
12712
|
-
candidates.push(
|
|
13362
|
+
candidates.push(join19(sourceDir, "package.json"));
|
|
12713
13363
|
}
|
|
12714
|
-
candidates.push(
|
|
13364
|
+
candidates.push(join19(process.cwd(), "package.json"));
|
|
12715
13365
|
for (const candidate of candidates) {
|
|
12716
13366
|
try {
|
|
12717
13367
|
const raw = await readFile3(candidate, "utf-8");
|
|
@@ -12735,9 +13385,9 @@ async function resolveBinaryPath(name) {
|
|
|
12735
13385
|
}
|
|
12736
13386
|
async function getRuntimeDiagnostics(options) {
|
|
12737
13387
|
const scriptPath = process.argv[1] ?? "";
|
|
12738
|
-
const invocationName =
|
|
13388
|
+
const invocationName = basename3(scriptPath || process.argv0 || "cleo");
|
|
12739
13389
|
const envChannel = normalizeChannel(process.env["CLEO_CHANNEL"]);
|
|
12740
|
-
const dataRoot = process.env["CLEO_HOME"] ??
|
|
13390
|
+
const dataRoot = process.env["CLEO_HOME"] ?? join19(homedir2(), ".cleo");
|
|
12741
13391
|
const versionInfo = await parseVersionFile(dataRoot);
|
|
12742
13392
|
const packageInfo = await getPackageInfo(versionInfo?.source);
|
|
12743
13393
|
const channel = envChannel ?? detectFromInvocation(invocationName) ?? normalizeChannel(versionInfo?.version.includes("-beta") ? "beta" : void 0) ?? detectFromDataRoot(dataRoot) ?? normalizeChannel(versionInfo?.mode.startsWith("dev") ? "dev" : void 0) ?? "stable";
|
|
@@ -12952,10 +13602,10 @@ async function systemLog(projectRoot, filters) {
|
|
|
12952
13602
|
}
|
|
12953
13603
|
async function queryAuditLogSqlite(projectRoot, filters) {
|
|
12954
13604
|
try {
|
|
12955
|
-
const { join:
|
|
12956
|
-
const { existsSync:
|
|
12957
|
-
const dbPath =
|
|
12958
|
-
if (!
|
|
13605
|
+
const { join: join45 } = await import("node:path");
|
|
13606
|
+
const { existsSync: existsSync44 } = await import("node:fs");
|
|
13607
|
+
const dbPath = join45(projectRoot, ".cleo", "tasks.db");
|
|
13608
|
+
if (!existsSync44(dbPath)) return null;
|
|
12959
13609
|
const { getDb: getDb2 } = await Promise.resolve().then(() => (init_sqlite(), sqlite_exports));
|
|
12960
13610
|
const { auditLog: auditLog2 } = await Promise.resolve().then(() => (init_schema(), schema_exports));
|
|
12961
13611
|
const { sql: sql4 } = await import("drizzle-orm");
|
|
@@ -13020,8 +13670,8 @@ async function queryAuditLogSqlite(projectRoot, filters) {
|
|
|
13020
13670
|
}
|
|
13021
13671
|
}
|
|
13022
13672
|
function queryAuditLogJsonl(projectRoot, filters) {
|
|
13023
|
-
const logPath =
|
|
13024
|
-
const raw =
|
|
13673
|
+
const logPath = getDataPath(projectRoot, "todo-log.jsonl");
|
|
13674
|
+
const raw = readLogFileEntries(logPath);
|
|
13025
13675
|
let entries = raw;
|
|
13026
13676
|
if (filters?.operation) {
|
|
13027
13677
|
entries = entries.filter((e) => e.operation === filters.operation);
|
|
@@ -13050,34 +13700,34 @@ function queryAuditLogJsonl(projectRoot, filters) {
|
|
|
13050
13700
|
}
|
|
13051
13701
|
function systemContext(projectRoot, params) {
|
|
13052
13702
|
try {
|
|
13053
|
-
const cleoDir =
|
|
13703
|
+
const cleoDir = join20(projectRoot, ".cleo");
|
|
13054
13704
|
let stateFile;
|
|
13055
13705
|
if (params?.session) {
|
|
13056
|
-
const sessionFile =
|
|
13057
|
-
stateFile =
|
|
13706
|
+
const sessionFile = join20(cleoDir, "context-states", `context-state-${params.session}.json`);
|
|
13707
|
+
stateFile = existsSync20(sessionFile) ? sessionFile : join20(cleoDir, ".context-state.json");
|
|
13058
13708
|
} else {
|
|
13059
|
-
const currentSessionPath =
|
|
13060
|
-
if (
|
|
13061
|
-
const currentSession =
|
|
13709
|
+
const currentSessionPath = join20(cleoDir, ".current-session");
|
|
13710
|
+
if (existsSync20(currentSessionPath)) {
|
|
13711
|
+
const currentSession = readFileSync13(currentSessionPath, "utf-8").trim();
|
|
13062
13712
|
if (currentSession) {
|
|
13063
|
-
const sessionFile =
|
|
13064
|
-
stateFile =
|
|
13713
|
+
const sessionFile = join20(cleoDir, "context-states", `context-state-${currentSession}.json`);
|
|
13714
|
+
stateFile = existsSync20(sessionFile) ? sessionFile : join20(cleoDir, ".context-state.json");
|
|
13065
13715
|
} else {
|
|
13066
|
-
stateFile =
|
|
13716
|
+
stateFile = join20(cleoDir, ".context-state.json");
|
|
13067
13717
|
}
|
|
13068
13718
|
} else {
|
|
13069
|
-
stateFile =
|
|
13719
|
+
stateFile = join20(cleoDir, ".context-state.json");
|
|
13070
13720
|
}
|
|
13071
13721
|
}
|
|
13072
13722
|
const sessions2 = [];
|
|
13073
|
-
const statesDir =
|
|
13074
|
-
if (
|
|
13723
|
+
const statesDir = join20(cleoDir, "context-states");
|
|
13724
|
+
if (existsSync20(statesDir)) {
|
|
13075
13725
|
for (const file of readdirSync5(statesDir)) {
|
|
13076
13726
|
if (file.startsWith("context-state-") && file.endsWith(".json")) {
|
|
13077
13727
|
try {
|
|
13078
|
-
const state = JSON.parse(
|
|
13728
|
+
const state = JSON.parse(readFileSync13(join20(statesDir, file), "utf-8"));
|
|
13079
13729
|
sessions2.push({
|
|
13080
|
-
file:
|
|
13730
|
+
file: basename4(file),
|
|
13081
13731
|
sessionId: state.sessionId ?? null,
|
|
13082
13732
|
percentage: state.contextWindow?.percentage ?? 0,
|
|
13083
13733
|
status: state.status ?? "unknown",
|
|
@@ -13088,10 +13738,10 @@ function systemContext(projectRoot, params) {
|
|
|
13088
13738
|
}
|
|
13089
13739
|
}
|
|
13090
13740
|
}
|
|
13091
|
-
const singletonFile =
|
|
13092
|
-
if (
|
|
13741
|
+
const singletonFile = join20(cleoDir, ".context-state.json");
|
|
13742
|
+
if (existsSync20(singletonFile)) {
|
|
13093
13743
|
try {
|
|
13094
|
-
const state = JSON.parse(
|
|
13744
|
+
const state = JSON.parse(readFileSync13(singletonFile, "utf-8"));
|
|
13095
13745
|
sessions2.push({
|
|
13096
13746
|
file: ".context-state.json",
|
|
13097
13747
|
sessionId: state.sessionId ?? "global",
|
|
@@ -13102,7 +13752,7 @@ function systemContext(projectRoot, params) {
|
|
|
13102
13752
|
} catch {
|
|
13103
13753
|
}
|
|
13104
13754
|
}
|
|
13105
|
-
if (!
|
|
13755
|
+
if (!existsSync20(stateFile)) {
|
|
13106
13756
|
return {
|
|
13107
13757
|
success: true,
|
|
13108
13758
|
data: {
|
|
@@ -13118,7 +13768,7 @@ function systemContext(projectRoot, params) {
|
|
|
13118
13768
|
};
|
|
13119
13769
|
}
|
|
13120
13770
|
try {
|
|
13121
|
-
const state = JSON.parse(
|
|
13771
|
+
const state = JSON.parse(readFileSync13(stateFile, "utf-8"));
|
|
13122
13772
|
const timestamp = state.timestamp;
|
|
13123
13773
|
const staleMs = state.staleAfterMs ?? 5e3;
|
|
13124
13774
|
const percentage = state.contextWindow?.percentage ?? 0;
|
|
@@ -13313,8 +13963,8 @@ init_json();
|
|
|
13313
13963
|
init_json();
|
|
13314
13964
|
init_paths();
|
|
13315
13965
|
import { chmod, mkdir as mkdir5, access as access2, writeFile as writeFile3, readFile as readFile5, copyFile as copyFile2, symlink, lstat, unlink as unlink3 } from "node:fs/promises";
|
|
13316
|
-
import { constants as fsConstants, existsSync as
|
|
13317
|
-
import { join as
|
|
13966
|
+
import { constants as fsConstants, existsSync as existsSync23, readFileSync as readFileSync16, readdirSync as readdirSync7 } from "node:fs";
|
|
13967
|
+
import { join as join25, resolve as resolve5, basename as basename5, dirname as dirname7 } from "node:path";
|
|
13318
13968
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
13319
13969
|
import { createHash as createHash5 } from "node:crypto";
|
|
13320
13970
|
import { homedir as homedir4 } from "node:os";
|
|
@@ -13330,7 +13980,7 @@ async function fileExists(path) {
|
|
|
13330
13980
|
}
|
|
13331
13981
|
}
|
|
13332
13982
|
async function stripCLEOBlocks(filePath) {
|
|
13333
|
-
if (!
|
|
13983
|
+
if (!existsSync23(filePath)) return;
|
|
13334
13984
|
const content = await readFile5(filePath, "utf8");
|
|
13335
13985
|
const stripped = content.replace(
|
|
13336
13986
|
/\n?<!-- CLEO:START -->[\s\S]*?<!-- CLEO:END -->\n?/g,
|
|
@@ -13366,31 +14016,67 @@ function createDefaultConfig() {
|
|
|
13366
14016
|
}
|
|
13367
14017
|
function getPackageRoot() {
|
|
13368
14018
|
const thisFile = fileURLToPath3(import.meta.url);
|
|
13369
|
-
return
|
|
14019
|
+
return resolve5(dirname7(thisFile), "..", "..");
|
|
13370
14020
|
}
|
|
13371
14021
|
function getGitignoreContent() {
|
|
13372
14022
|
try {
|
|
13373
14023
|
const packageRoot = getPackageRoot();
|
|
13374
|
-
const templatePath =
|
|
13375
|
-
if (
|
|
13376
|
-
return
|
|
14024
|
+
const templatePath = join25(packageRoot, "templates", "cleo-gitignore");
|
|
14025
|
+
if (existsSync23(templatePath)) {
|
|
14026
|
+
return readFileSync16(templatePath, "utf-8");
|
|
13377
14027
|
}
|
|
13378
14028
|
} catch {
|
|
13379
14029
|
}
|
|
13380
14030
|
return CLEO_GITIGNORE_FALLBACK;
|
|
13381
14031
|
}
|
|
13382
|
-
var CLEO_GITIGNORE_FALLBACK = `#
|
|
13383
|
-
#
|
|
13384
|
-
|
|
13385
|
-
|
|
13386
|
-
|
|
13387
|
-
|
|
13388
|
-
|
|
13389
|
-
|
|
13390
|
-
.
|
|
14032
|
+
var CLEO_GITIGNORE_FALLBACK = `# .cleo/.gitignore \u2014 Deny-by-default for CLEO project data
|
|
14033
|
+
# Ignore everything, then explicitly allow only tracked files.
|
|
14034
|
+
|
|
14035
|
+
# Step 1: Ignore everything
|
|
14036
|
+
*
|
|
14037
|
+
|
|
14038
|
+
# Allow list
|
|
14039
|
+
!.gitignore
|
|
14040
|
+
!config.json
|
|
14041
|
+
!project-context.json
|
|
14042
|
+
!project-info.json
|
|
14043
|
+
!setup-otel.sh
|
|
14044
|
+
!DATA-SAFETY-IMPLEMENTATION-SUMMARY.md
|
|
14045
|
+
!schemas/
|
|
14046
|
+
!schemas/**
|
|
14047
|
+
!templates/
|
|
14048
|
+
!templates/**
|
|
14049
|
+
!adrs/
|
|
14050
|
+
!adrs/**
|
|
14051
|
+
!rcasd/
|
|
14052
|
+
!rcasd/**
|
|
14053
|
+
!agent-outputs/
|
|
14054
|
+
!agent-outputs/**
|
|
14055
|
+
|
|
14056
|
+
# Explicit deny safety net
|
|
14057
|
+
*.db
|
|
14058
|
+
*.db-shm
|
|
14059
|
+
*.db-wal
|
|
14060
|
+
*.db-journal
|
|
14061
|
+
log.json
|
|
14062
|
+
tasks-log.jsonl
|
|
14063
|
+
todo-log.jsonl
|
|
14064
|
+
bypass-log.json
|
|
14065
|
+
qa-log.json
|
|
14066
|
+
.deps-cache/
|
|
14067
|
+
.context-alert-state.json
|
|
14068
|
+
.context-state*.json
|
|
14069
|
+
context-states/
|
|
14070
|
+
.git-checkpoint-state
|
|
14071
|
+
.migration-state.json
|
|
14072
|
+
migrations.json
|
|
14073
|
+
sync/
|
|
14074
|
+
metrics/
|
|
14075
|
+
.backups/
|
|
14076
|
+
backups/
|
|
13391
14077
|
`;
|
|
13392
14078
|
async function removeCleoFromRootGitignore(projectRoot) {
|
|
13393
|
-
const rootGitignorePath =
|
|
14079
|
+
const rootGitignorePath = join25(projectRoot, ".gitignore");
|
|
13394
14080
|
if (!await fileExists(rootGitignorePath)) {
|
|
13395
14081
|
return false;
|
|
13396
14082
|
}
|
|
@@ -13411,8 +14097,8 @@ function generateProjectHash2(projectPath) {
|
|
|
13411
14097
|
}
|
|
13412
14098
|
function getCleoVersion() {
|
|
13413
14099
|
try {
|
|
13414
|
-
const pkgPath =
|
|
13415
|
-
const pkg = JSON.parse(
|
|
14100
|
+
const pkgPath = join25(getPackageRoot(), "package.json");
|
|
14101
|
+
const pkg = JSON.parse(readFileSync16(pkgPath, "utf-8"));
|
|
13416
14102
|
return pkg.version ?? "0.0.0";
|
|
13417
14103
|
} catch {
|
|
13418
14104
|
return "0.0.0";
|
|
@@ -13420,9 +14106,9 @@ function getCleoVersion() {
|
|
|
13420
14106
|
}
|
|
13421
14107
|
function getInjectionTemplateContent() {
|
|
13422
14108
|
const packageRoot = getPackageRoot();
|
|
13423
|
-
const packageTemplatePath =
|
|
13424
|
-
if (
|
|
13425
|
-
return
|
|
14109
|
+
const packageTemplatePath = join25(packageRoot, "templates", "CLEO-INJECTION.md");
|
|
14110
|
+
if (existsSync23(packageTemplatePath)) {
|
|
14111
|
+
return readFileSync16(packageTemplatePath, "utf-8");
|
|
13426
14112
|
}
|
|
13427
14113
|
return null;
|
|
13428
14114
|
}
|
|
@@ -13435,27 +14121,27 @@ async function initCoreFiles(cleoDir, _projectName, force, created, skipped) {
|
|
|
13435
14121
|
await saveJson(configPath, createDefaultConfig());
|
|
13436
14122
|
created.push("config.json");
|
|
13437
14123
|
}
|
|
13438
|
-
const legacySequencePath =
|
|
14124
|
+
const legacySequencePath = join25(cleoDir, ".sequence");
|
|
13439
14125
|
try {
|
|
13440
14126
|
await unlink3(legacySequencePath);
|
|
13441
14127
|
} catch {
|
|
13442
14128
|
}
|
|
13443
|
-
const legacySequenceJsonPath =
|
|
14129
|
+
const legacySequenceJsonPath = join25(cleoDir, ".sequence.json");
|
|
13444
14130
|
try {
|
|
13445
14131
|
await unlink3(legacySequenceJsonPath);
|
|
13446
14132
|
} catch {
|
|
13447
14133
|
}
|
|
13448
|
-
const backupDir =
|
|
13449
|
-
await mkdir5(
|
|
13450
|
-
await mkdir5(
|
|
14134
|
+
const backupDir = join25(cleoDir, "backups");
|
|
14135
|
+
await mkdir5(join25(backupDir, "operational"), { recursive: true });
|
|
14136
|
+
await mkdir5(join25(backupDir, "safety"), { recursive: true });
|
|
13451
14137
|
try {
|
|
13452
14138
|
const { getDb: getDb2 } = await Promise.resolve().then(() => (init_sqlite(), sqlite_exports));
|
|
13453
|
-
await getDb2(
|
|
14139
|
+
await getDb2(join25(cleoDir, ".."));
|
|
13454
14140
|
created.push("tasks.db");
|
|
13455
14141
|
} catch (err) {
|
|
13456
14142
|
created.push(`tasks.db (deferred: ${err instanceof Error ? err.message : String(err)})`);
|
|
13457
14143
|
}
|
|
13458
|
-
const gitignorePath =
|
|
14144
|
+
const gitignorePath = join25(cleoDir, ".gitignore");
|
|
13459
14145
|
if (await fileExists(gitignorePath) && !force) {
|
|
13460
14146
|
skipped.push(".gitignore");
|
|
13461
14147
|
} else {
|
|
@@ -13464,8 +14150,8 @@ async function initCoreFiles(cleoDir, _projectName, force, created, skipped) {
|
|
|
13464
14150
|
}
|
|
13465
14151
|
}
|
|
13466
14152
|
async function initCleoGitRepo(cleoDir, created, warnings) {
|
|
13467
|
-
const cleoGitDir =
|
|
13468
|
-
if (
|
|
14153
|
+
const cleoGitDir = join25(cleoDir, ".git");
|
|
14154
|
+
if (existsSync23(cleoGitDir)) {
|
|
13469
14155
|
return;
|
|
13470
14156
|
}
|
|
13471
14157
|
const gitEnv = {
|
|
@@ -13483,11 +14169,11 @@ async function initCleoGitRepo(cleoDir, created, warnings) {
|
|
|
13483
14169
|
}
|
|
13484
14170
|
}
|
|
13485
14171
|
async function initSchemas(cleoDir, force, created, warnings) {
|
|
13486
|
-
const schemasDir =
|
|
14172
|
+
const schemasDir = join25(cleoDir, "schemas");
|
|
13487
14173
|
await mkdir5(schemasDir, { recursive: true });
|
|
13488
14174
|
const packageRoot = getPackageRoot();
|
|
13489
|
-
const sourceSchemaDir =
|
|
13490
|
-
if (!
|
|
14175
|
+
const sourceSchemaDir = join25(packageRoot, "schemas");
|
|
14176
|
+
if (!existsSync23(sourceSchemaDir)) {
|
|
13491
14177
|
warnings.push("schemas/ directory not found in package root, skipping schema installation");
|
|
13492
14178
|
return;
|
|
13493
14179
|
}
|
|
@@ -13498,9 +14184,9 @@ async function initSchemas(cleoDir, force, created, warnings) {
|
|
|
13498
14184
|
];
|
|
13499
14185
|
let copiedCount = 0;
|
|
13500
14186
|
for (const schemaFile of coreSchemas) {
|
|
13501
|
-
const sourcePath =
|
|
13502
|
-
const destPath =
|
|
13503
|
-
if (!
|
|
14187
|
+
const sourcePath = join25(sourceSchemaDir, schemaFile);
|
|
14188
|
+
const destPath = join25(schemasDir, schemaFile);
|
|
14189
|
+
if (!existsSync23(sourcePath)) {
|
|
13504
14190
|
continue;
|
|
13505
14191
|
}
|
|
13506
14192
|
if (await fileExists(destPath) && !force) {
|
|
@@ -13518,27 +14204,27 @@ async function initSchemas(cleoDir, force, created, warnings) {
|
|
|
13518
14204
|
}
|
|
13519
14205
|
}
|
|
13520
14206
|
async function initGitHooks(projRoot, force, created, warnings) {
|
|
13521
|
-
const gitHooksDir =
|
|
13522
|
-
if (!
|
|
14207
|
+
const gitHooksDir = join25(projRoot, ".git", "hooks");
|
|
14208
|
+
if (!existsSync23(join25(projRoot, ".git"))) {
|
|
13523
14209
|
warnings.push("No .git/ directory found, skipping git hook installation");
|
|
13524
14210
|
return;
|
|
13525
14211
|
}
|
|
13526
14212
|
await mkdir5(gitHooksDir, { recursive: true });
|
|
13527
14213
|
const packageRoot = getPackageRoot();
|
|
13528
|
-
const sourceDir =
|
|
13529
|
-
if (!
|
|
14214
|
+
const sourceDir = join25(packageRoot, "templates", "git-hooks");
|
|
14215
|
+
if (!existsSync23(sourceDir)) {
|
|
13530
14216
|
warnings.push("templates/git-hooks/ not found in package root, skipping git hook installation");
|
|
13531
14217
|
return;
|
|
13532
14218
|
}
|
|
13533
14219
|
const hooks = ["commit-msg", "pre-commit"];
|
|
13534
14220
|
let installedCount = 0;
|
|
13535
14221
|
for (const hook of hooks) {
|
|
13536
|
-
const sourcePath =
|
|
13537
|
-
const destPath =
|
|
13538
|
-
if (!
|
|
14222
|
+
const sourcePath = join25(sourceDir, hook);
|
|
14223
|
+
const destPath = join25(gitHooksDir, hook);
|
|
14224
|
+
if (!existsSync23(sourcePath)) {
|
|
13539
14225
|
continue;
|
|
13540
14226
|
}
|
|
13541
|
-
if (
|
|
14227
|
+
if (existsSync23(destPath) && !force) {
|
|
13542
14228
|
continue;
|
|
13543
14229
|
}
|
|
13544
14230
|
try {
|
|
@@ -13554,7 +14240,7 @@ async function initGitHooks(projRoot, force, created, warnings) {
|
|
|
13554
14240
|
}
|
|
13555
14241
|
}
|
|
13556
14242
|
async function initProjectInfo(cleoDir, projectRoot, force, created, skipped) {
|
|
13557
|
-
const projectInfoPath =
|
|
14243
|
+
const projectInfoPath = join25(cleoDir, "project-info.json");
|
|
13558
14244
|
if (await fileExists(projectInfoPath) && !force) {
|
|
13559
14245
|
skipped.push("project-info.json");
|
|
13560
14246
|
return;
|
|
@@ -13603,40 +14289,40 @@ async function initInjection(projectRoot, created, warnings) {
|
|
|
13603
14289
|
return;
|
|
13604
14290
|
}
|
|
13605
14291
|
for (const provider of providers) {
|
|
13606
|
-
const instructFile =
|
|
14292
|
+
const instructFile = join25(projectRoot, provider.pathProject, provider.instructFile);
|
|
13607
14293
|
await stripCLEOBlocks(instructFile);
|
|
13608
14294
|
}
|
|
13609
|
-
await stripCLEOBlocks(
|
|
14295
|
+
await stripCLEOBlocks(join25(projectRoot, "AGENTS.md"));
|
|
13610
14296
|
const injectionContent = buildInjectionContent2({ references: ["@AGENTS.md"] });
|
|
13611
14297
|
const results = await injectAll2(providers, projectRoot, "project", injectionContent);
|
|
13612
14298
|
const injected = [];
|
|
13613
14299
|
for (const [filePath, action] of results) {
|
|
13614
|
-
const fileName =
|
|
14300
|
+
const fileName = basename5(filePath);
|
|
13615
14301
|
injected.push(`${fileName} (${action})`);
|
|
13616
14302
|
}
|
|
13617
14303
|
if (injected.length > 0) {
|
|
13618
14304
|
created.push(`injection: ${injected.join(", ")}`);
|
|
13619
14305
|
}
|
|
13620
|
-
const agentsMdPath =
|
|
14306
|
+
const agentsMdPath = join25(projectRoot, "AGENTS.md");
|
|
13621
14307
|
const agentsMdLines = ["@~/.cleo/templates/CLEO-INJECTION.md"];
|
|
13622
|
-
const projectContextPath =
|
|
13623
|
-
if (
|
|
14308
|
+
const projectContextPath = join25(projectRoot, ".cleo", "project-context.json");
|
|
14309
|
+
if (existsSync23(projectContextPath)) {
|
|
13624
14310
|
agentsMdLines.push("@.cleo/project-context.json");
|
|
13625
14311
|
}
|
|
13626
14312
|
const agentsAction = await inject(agentsMdPath, agentsMdLines.join("\n"));
|
|
13627
14313
|
created.push(`AGENTS.md CLEO content (${agentsAction})`);
|
|
13628
14314
|
const content = getInjectionTemplateContent();
|
|
13629
14315
|
if (content) {
|
|
13630
|
-
const globalTemplatesDir =
|
|
14316
|
+
const globalTemplatesDir = join25(getCleoHome(), "templates");
|
|
13631
14317
|
await mkdir5(globalTemplatesDir, { recursive: true });
|
|
13632
|
-
const globalPath =
|
|
13633
|
-
if (!
|
|
14318
|
+
const globalPath = join25(globalTemplatesDir, "CLEO-INJECTION.md");
|
|
14319
|
+
if (!existsSync23(globalPath)) {
|
|
13634
14320
|
await writeFile3(globalPath, content);
|
|
13635
14321
|
}
|
|
13636
14322
|
}
|
|
13637
14323
|
try {
|
|
13638
|
-
const globalAgentsDir =
|
|
13639
|
-
const globalAgentsMd =
|
|
14324
|
+
const globalAgentsDir = join25(homedir4(), ".agents");
|
|
14325
|
+
const globalAgentsMd = join25(globalAgentsDir, "AGENTS.md");
|
|
13640
14326
|
await mkdir5(globalAgentsDir, { recursive: true });
|
|
13641
14327
|
await inject(globalAgentsMd, "@~/.cleo/templates/CLEO-INJECTION.md");
|
|
13642
14328
|
} catch {
|
|
@@ -13647,13 +14333,13 @@ async function initInjection(projectRoot, created, warnings) {
|
|
|
13647
14333
|
}
|
|
13648
14334
|
async function initAgentDefinition(created, warnings) {
|
|
13649
14335
|
const packageRoot = getPackageRoot();
|
|
13650
|
-
const agentSourceDir =
|
|
13651
|
-
if (!
|
|
14336
|
+
const agentSourceDir = join25(packageRoot, "agents", "cleo-subagent");
|
|
14337
|
+
if (!existsSync23(agentSourceDir)) {
|
|
13652
14338
|
warnings.push("agents/cleo-subagent/ not found in package, skipping agent definition install");
|
|
13653
14339
|
return;
|
|
13654
14340
|
}
|
|
13655
|
-
const globalAgentsDir =
|
|
13656
|
-
await mkdir5(
|
|
14341
|
+
const globalAgentsDir = join25(homedir4(), ".agents", "agents", "cleo-subagent");
|
|
14342
|
+
await mkdir5(dirname7(globalAgentsDir), { recursive: true });
|
|
13657
14343
|
try {
|
|
13658
14344
|
try {
|
|
13659
14345
|
const stat2 = await lstat(globalAgentsDir);
|
|
@@ -13669,7 +14355,7 @@ async function initAgentDefinition(created, warnings) {
|
|
|
13669
14355
|
await mkdir5(globalAgentsDir, { recursive: true });
|
|
13670
14356
|
const files = readdirSync7(agentSourceDir);
|
|
13671
14357
|
for (const file of files) {
|
|
13672
|
-
await copyFile2(
|
|
14358
|
+
await copyFile2(join25(agentSourceDir, file), join25(globalAgentsDir, file));
|
|
13673
14359
|
}
|
|
13674
14360
|
created.push("agent: cleo-subagent (copied)");
|
|
13675
14361
|
} catch (copyErr) {
|
|
@@ -13721,12 +14407,12 @@ async function initCoreSkills(created, warnings) {
|
|
|
13721
14407
|
let ctSkillsRoot = null;
|
|
13722
14408
|
try {
|
|
13723
14409
|
const packageRoot = getPackageRoot();
|
|
13724
|
-
const bundledPath =
|
|
13725
|
-
if (
|
|
14410
|
+
const bundledPath = join25(packageRoot, "packages", "ct-skills");
|
|
14411
|
+
if (existsSync23(join25(bundledPath, "skills.json"))) {
|
|
13726
14412
|
ctSkillsRoot = bundledPath;
|
|
13727
14413
|
} else {
|
|
13728
|
-
const ctSkillsPath =
|
|
13729
|
-
if (
|
|
14414
|
+
const ctSkillsPath = join25(packageRoot, "node_modules", "@cleocode", "ct-skills");
|
|
14415
|
+
if (existsSync23(join25(ctSkillsPath, "skills.json"))) {
|
|
13730
14416
|
ctSkillsRoot = ctSkillsPath;
|
|
13731
14417
|
}
|
|
13732
14418
|
}
|
|
@@ -13741,14 +14427,14 @@ async function initCoreSkills(created, warnings) {
|
|
|
13741
14427
|
} catch {
|
|
13742
14428
|
warnings.push("Failed to register skill library with CAAMP");
|
|
13743
14429
|
}
|
|
13744
|
-
const catalogPath =
|
|
13745
|
-
const catalog2 = JSON.parse(
|
|
14430
|
+
const catalogPath = join25(ctSkillsRoot, "skills.json");
|
|
14431
|
+
const catalog2 = JSON.parse(readFileSync16(catalogPath, "utf-8"));
|
|
13746
14432
|
const skills = catalog2.skills ?? [];
|
|
13747
14433
|
const coreSkills = skills.filter((s) => s.tier <= 2);
|
|
13748
14434
|
const installed = [];
|
|
13749
14435
|
for (const skill of coreSkills) {
|
|
13750
|
-
const skillSourceDir =
|
|
13751
|
-
if (!
|
|
14436
|
+
const skillSourceDir = dirname7(join25(ctSkillsRoot, skill.path));
|
|
14437
|
+
if (!existsSync23(skillSourceDir)) {
|
|
13752
14438
|
continue;
|
|
13753
14439
|
}
|
|
13754
14440
|
try {
|
|
@@ -13783,7 +14469,7 @@ async function initProjectDetect(cleoDir, projectRoot, created, warnings) {
|
|
|
13783
14469
|
try {
|
|
13784
14470
|
const { detectProjectType: detectProjectType2 } = await Promise.resolve().then(() => (init_project_detect(), project_detect_exports));
|
|
13785
14471
|
const info = detectProjectType2(projectRoot);
|
|
13786
|
-
const contextPath =
|
|
14472
|
+
const contextPath = join25(cleoDir, "project-context.json");
|
|
13787
14473
|
const context = {
|
|
13788
14474
|
...info,
|
|
13789
14475
|
detectedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -13856,8 +14542,8 @@ async function initProject(opts = {}) {
|
|
|
13856
14542
|
async function getVersion(projectRoot) {
|
|
13857
14543
|
const root = projectRoot ?? getProjectRoot();
|
|
13858
14544
|
const versionPaths = [
|
|
13859
|
-
|
|
13860
|
-
|
|
14545
|
+
join25(root, "VERSION"),
|
|
14546
|
+
join25(root, "..", "VERSION")
|
|
13861
14547
|
];
|
|
13862
14548
|
for (const versionPath of versionPaths) {
|
|
13863
14549
|
try {
|
|
@@ -13869,7 +14555,7 @@ async function getVersion(projectRoot) {
|
|
|
13869
14555
|
} catch {
|
|
13870
14556
|
}
|
|
13871
14557
|
}
|
|
13872
|
-
const pkg = await readJson(
|
|
14558
|
+
const pkg = await readJson(join25(root, "package.json"));
|
|
13873
14559
|
if (pkg?.version) {
|
|
13874
14560
|
return { version: pkg.version };
|
|
13875
14561
|
}
|
|
@@ -13912,8 +14598,8 @@ async function getVersion2(projectRoot) {
|
|
|
13912
14598
|
init_paths();
|
|
13913
14599
|
init_file_utils();
|
|
13914
14600
|
init_schema();
|
|
13915
|
-
import { existsSync as
|
|
13916
|
-
import { join as
|
|
14601
|
+
import { existsSync as existsSync24, mkdirSync as mkdirSync8, readdirSync as readdirSync8 } from "node:fs";
|
|
14602
|
+
import { join as join26 } from "node:path";
|
|
13917
14603
|
|
|
13918
14604
|
// src/core/lifecycle/stages.ts
|
|
13919
14605
|
var PIPELINE_STAGES = [
|
|
@@ -14059,7 +14745,7 @@ var DEFAULT_LIFECYCLE_DATA_DIR = "rcasd";
|
|
|
14059
14745
|
function resolveLifecycleDir(epicId, cwd) {
|
|
14060
14746
|
const cleoDir = getCleoDirAbsolute(cwd);
|
|
14061
14747
|
for (const dirName of LIFECYCLE_DATA_DIRS) {
|
|
14062
|
-
if (
|
|
14748
|
+
if (existsSync24(join26(cleoDir, dirName, epicId))) {
|
|
14063
14749
|
return dirName;
|
|
14064
14750
|
}
|
|
14065
14751
|
}
|
|
@@ -14068,18 +14754,18 @@ function resolveLifecycleDir(epicId, cwd) {
|
|
|
14068
14754
|
function getRcsdDir(epicId, cwd) {
|
|
14069
14755
|
const cleoDir = getCleoDirAbsolute(cwd);
|
|
14070
14756
|
const dirName = resolveLifecycleDir(epicId, cwd);
|
|
14071
|
-
return
|
|
14757
|
+
return join26(cleoDir, dirName, epicId);
|
|
14072
14758
|
}
|
|
14073
14759
|
function getRcsdManifestPath(epicId, cwd) {
|
|
14074
|
-
return
|
|
14760
|
+
return join26(getRcsdDir(epicId, cwd), "_manifest.json");
|
|
14075
14761
|
}
|
|
14076
14762
|
function readManifestSync(epicId, cwd) {
|
|
14077
|
-
return
|
|
14763
|
+
return readJsonFile(getRcsdManifestPath(epicId, cwd));
|
|
14078
14764
|
}
|
|
14079
14765
|
function writeManifestSync(epicId, manifest, cwd) {
|
|
14080
14766
|
const dir = getRcsdDir(epicId, cwd);
|
|
14081
|
-
if (!
|
|
14082
|
-
|
|
14767
|
+
if (!existsSync24(dir)) {
|
|
14768
|
+
mkdirSync8(dir, { recursive: true });
|
|
14083
14769
|
}
|
|
14084
14770
|
writeJsonFileAtomic(getRcsdManifestPath(epicId, cwd), manifest);
|
|
14085
14771
|
}
|
|
@@ -14533,16 +15219,16 @@ init_platform();
|
|
|
14533
15219
|
// src/core/validation/validate-ops.ts
|
|
14534
15220
|
init_paths();
|
|
14535
15221
|
init_data_accessor();
|
|
14536
|
-
import { readFileSync as
|
|
15222
|
+
import { readFileSync as readFileSync18, existsSync as existsSync26, appendFileSync as appendFileSync3, mkdirSync as mkdirSync9 } from "node:fs";
|
|
14537
15223
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
14538
|
-
import { join as
|
|
15224
|
+
import { join as join28, dirname as dirname8, resolve as resolve6 } from "node:path";
|
|
14539
15225
|
|
|
14540
|
-
// src/
|
|
15226
|
+
// src/core/validation/schema-validator.ts
|
|
14541
15227
|
init_validation_schemas();
|
|
14542
15228
|
import AjvModule2 from "ajv";
|
|
14543
15229
|
import addFormatsModule2 from "ajv-formats";
|
|
14544
|
-
import { readFileSync as
|
|
14545
|
-
import { join as
|
|
15230
|
+
import { readFileSync as readFileSync17, existsSync as existsSync25 } from "fs";
|
|
15231
|
+
import { join as join27 } from "path";
|
|
14546
15232
|
var Ajv2 = AjvModule2.default || AjvModule2;
|
|
14547
15233
|
var addFormats2 = addFormatsModule2.default || addFormatsModule2;
|
|
14548
15234
|
var schemaCache2 = /* @__PURE__ */ new Map();
|
|
@@ -14566,14 +15252,14 @@ function resolveSchemaPath2(schemaType) {
|
|
|
14566
15252
|
const filename = `${schemaType}.schema.json`;
|
|
14567
15253
|
const projectRoot = process.env.CLEO_ROOT || process.cwd();
|
|
14568
15254
|
const paths = [
|
|
14569
|
-
|
|
14570
|
-
|
|
14571
|
-
// relative from dist/
|
|
14572
|
-
|
|
14573
|
-
// relative from dist/
|
|
15255
|
+
join27(projectRoot, "schemas", filename),
|
|
15256
|
+
join27(__dirname, "..", "..", "..", "schemas", filename),
|
|
15257
|
+
// relative from dist/core/validation/
|
|
15258
|
+
join27(__dirname, "..", "..", "schemas", filename)
|
|
15259
|
+
// relative from dist/core/
|
|
14574
15260
|
];
|
|
14575
15261
|
for (const p of paths) {
|
|
14576
|
-
if (
|
|
15262
|
+
if (existsSync25(p)) {
|
|
14577
15263
|
return p;
|
|
14578
15264
|
}
|
|
14579
15265
|
}
|
|
@@ -14589,7 +15275,7 @@ function getValidator(schemaType) {
|
|
|
14589
15275
|
return null;
|
|
14590
15276
|
}
|
|
14591
15277
|
try {
|
|
14592
|
-
const schemaContent =
|
|
15278
|
+
const schemaContent = readFileSync17(schemaPath, "utf-8");
|
|
14593
15279
|
const schema = JSON.parse(schemaContent);
|
|
14594
15280
|
const ajv = getAjv2();
|
|
14595
15281
|
const validate = ajv.compile(schema);
|
|
@@ -14627,7 +15313,7 @@ function validateSchema(schemaType, data) {
|
|
|
14627
15313
|
return { valid: false, errors };
|
|
14628
15314
|
}
|
|
14629
15315
|
|
|
14630
|
-
// src/
|
|
15316
|
+
// src/core/validation/validation-rules.ts
|
|
14631
15317
|
function validateTitleDescription(title, description) {
|
|
14632
15318
|
const violations = [];
|
|
14633
15319
|
if (!title || title.trim().length === 0) {
|
|
@@ -14761,35 +15447,6 @@ function hasErrors(violations) {
|
|
|
14761
15447
|
return violations.some((v) => v.severity === "error");
|
|
14762
15448
|
}
|
|
14763
15449
|
|
|
14764
|
-
// src/core/tasks/dependency-check.ts
|
|
14765
|
-
function detectCircularDeps(taskId, tasks2) {
|
|
14766
|
-
const taskMap = new Map(tasks2.map((t) => [t.id, t]));
|
|
14767
|
-
const visited = /* @__PURE__ */ new Set();
|
|
14768
|
-
const recursionStack = /* @__PURE__ */ new Set();
|
|
14769
|
-
const path = [];
|
|
14770
|
-
function dfs(id) {
|
|
14771
|
-
visited.add(id);
|
|
14772
|
-
recursionStack.add(id);
|
|
14773
|
-
path.push(id);
|
|
14774
|
-
const task = taskMap.get(id);
|
|
14775
|
-
if (task?.depends) {
|
|
14776
|
-
for (const depId of task.depends) {
|
|
14777
|
-
if (!visited.has(depId)) {
|
|
14778
|
-
const cycle = dfs(depId);
|
|
14779
|
-
if (cycle.length > 0) return cycle;
|
|
14780
|
-
} else if (recursionStack.has(depId)) {
|
|
14781
|
-
const cycleStart = path.indexOf(depId);
|
|
14782
|
-
return [...path.slice(cycleStart), depId];
|
|
14783
|
-
}
|
|
14784
|
-
}
|
|
14785
|
-
}
|
|
14786
|
-
path.pop();
|
|
14787
|
-
recursionStack.delete(id);
|
|
14788
|
-
return [];
|
|
14789
|
-
}
|
|
14790
|
-
return dfs(taskId);
|
|
14791
|
-
}
|
|
14792
|
-
|
|
14793
15450
|
// src/core/validation/validate-ops.ts
|
|
14794
15451
|
init_json();
|
|
14795
15452
|
init_status_registry();
|
|
@@ -14798,9 +15455,9 @@ init_status_registry();
|
|
|
14798
15455
|
import { isNull as isNull2, sql as sql3 } from "drizzle-orm";
|
|
14799
15456
|
|
|
14800
15457
|
// src/core/validation/validate-ops.ts
|
|
14801
|
-
function
|
|
15458
|
+
function readJsonFile2(filePath) {
|
|
14802
15459
|
try {
|
|
14803
|
-
const raw =
|
|
15460
|
+
const raw = readFileSync18(filePath, "utf-8");
|
|
14804
15461
|
return JSON.parse(raw);
|
|
14805
15462
|
} catch {
|
|
14806
15463
|
return null;
|
|
@@ -14815,11 +15472,11 @@ async function coreValidateSchema(type, data, projectRoot) {
|
|
|
14815
15472
|
throw new Error(`Unknown schema type: ${type}. Valid types: ${validTypes.join(", ")}`);
|
|
14816
15473
|
}
|
|
14817
15474
|
if (type === "config") {
|
|
14818
|
-
const filePath =
|
|
14819
|
-
if (!
|
|
15475
|
+
const filePath = join28(projectRoot, ".cleo", "config.json");
|
|
15476
|
+
if (!existsSync26(filePath)) {
|
|
14820
15477
|
throw new Error("File not found: .cleo/config.json");
|
|
14821
15478
|
}
|
|
14822
|
-
const configData = data ??
|
|
15479
|
+
const configData = data ?? readJsonFile2(filePath);
|
|
14823
15480
|
const result = validateSchema("config", configData);
|
|
14824
15481
|
return { type, valid: result.valid, errors: result.errors, errorCount: result.errors.length };
|
|
14825
15482
|
}
|
|
@@ -14953,7 +15610,7 @@ async function coreValidateProtocol(taskId, protocolType, projectRoot) {
|
|
|
14953
15610
|
}
|
|
14954
15611
|
function coreValidateManifest(projectRoot) {
|
|
14955
15612
|
const manifestPath = getManifestPath(projectRoot);
|
|
14956
|
-
if (!
|
|
15613
|
+
if (!existsSync26(manifestPath)) {
|
|
14957
15614
|
return {
|
|
14958
15615
|
valid: true,
|
|
14959
15616
|
totalEntries: 0,
|
|
@@ -14963,7 +15620,7 @@ function coreValidateManifest(projectRoot) {
|
|
|
14963
15620
|
message: "No manifest file found"
|
|
14964
15621
|
};
|
|
14965
15622
|
}
|
|
14966
|
-
const content =
|
|
15623
|
+
const content = readFileSync18(manifestPath, "utf-8");
|
|
14967
15624
|
const lines = content.split("\n").filter((l) => l.trim());
|
|
14968
15625
|
let validCount = 0;
|
|
14969
15626
|
let invalidCount = 0;
|
|
@@ -15003,11 +15660,11 @@ function coreValidateOutput(filePath, taskId, projectRoot) {
|
|
|
15003
15660
|
if (!filePath) {
|
|
15004
15661
|
throw new Error("filePath is required");
|
|
15005
15662
|
}
|
|
15006
|
-
const fullPath =
|
|
15007
|
-
if (!
|
|
15663
|
+
const fullPath = resolve6(projectRoot, filePath);
|
|
15664
|
+
if (!existsSync26(fullPath)) {
|
|
15008
15665
|
throw new Error(`Output file not found: ${filePath}`);
|
|
15009
15666
|
}
|
|
15010
|
-
const content =
|
|
15667
|
+
const content = readFileSync18(fullPath, "utf-8");
|
|
15011
15668
|
const issues = [];
|
|
15012
15669
|
if (!content.includes("# ")) {
|
|
15013
15670
|
issues.push({ code: "O_MISSING_TITLE", message: "Output file should have a markdown title", severity: "warning" });
|
|
@@ -15027,11 +15684,11 @@ function coreValidateOutput(filePath, taskId, projectRoot) {
|
|
|
15027
15684
|
};
|
|
15028
15685
|
}
|
|
15029
15686
|
function parseComplianceEntries(projectRoot) {
|
|
15030
|
-
const compliancePath =
|
|
15031
|
-
if (!
|
|
15687
|
+
const compliancePath = join28(projectRoot, ".cleo", "metrics", "COMPLIANCE.jsonl");
|
|
15688
|
+
if (!existsSync26(compliancePath)) {
|
|
15032
15689
|
return [];
|
|
15033
15690
|
}
|
|
15034
|
-
const content =
|
|
15691
|
+
const content = readFileSync18(compliancePath, "utf-8");
|
|
15035
15692
|
const entries = [];
|
|
15036
15693
|
for (const line of content.split("\n")) {
|
|
15037
15694
|
const trimmed = line.trim();
|
|
@@ -15091,10 +15748,10 @@ function coreComplianceRecord(taskId, result, protocol, violations, projectRoot)
|
|
|
15091
15748
|
if (!validResults.includes(result)) {
|
|
15092
15749
|
throw new Error(`Invalid result: ${result}. Valid: ${validResults.join(", ")}`);
|
|
15093
15750
|
}
|
|
15094
|
-
const compliancePath =
|
|
15095
|
-
const dir =
|
|
15096
|
-
if (!
|
|
15097
|
-
|
|
15751
|
+
const compliancePath = join28(projectRoot, ".cleo", "metrics", "COMPLIANCE.jsonl");
|
|
15752
|
+
const dir = dirname8(compliancePath);
|
|
15753
|
+
if (!existsSync26(dir)) {
|
|
15754
|
+
mkdirSync9(dir, { recursive: true });
|
|
15098
15755
|
}
|
|
15099
15756
|
const entry = {
|
|
15100
15757
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -15113,10 +15770,10 @@ function coreComplianceRecord(taskId, result, protocol, violations, projectRoot)
|
|
|
15113
15770
|
};
|
|
15114
15771
|
}
|
|
15115
15772
|
function coreTestStatus(projectRoot) {
|
|
15116
|
-
const testDir =
|
|
15117
|
-
const mcpTestDir =
|
|
15118
|
-
const hasBatsTests =
|
|
15119
|
-
const hasMcpTests =
|
|
15773
|
+
const testDir = join28(projectRoot, "tests");
|
|
15774
|
+
const mcpTestDir = join28(projectRoot, "src", "mcp", "__tests__");
|
|
15775
|
+
const hasBatsTests = existsSync26(testDir);
|
|
15776
|
+
const hasMcpTests = existsSync26(mcpTestDir);
|
|
15120
15777
|
return {
|
|
15121
15778
|
batsTests: {
|
|
15122
15779
|
available: hasBatsTests,
|
|
@@ -15258,8 +15915,8 @@ async function coreCoherenceCheck(projectRoot) {
|
|
|
15258
15915
|
};
|
|
15259
15916
|
}
|
|
15260
15917
|
function coreTestRun(params, projectRoot) {
|
|
15261
|
-
const hasVitest =
|
|
15262
|
-
const hasBats =
|
|
15918
|
+
const hasVitest = existsSync26(join28(projectRoot, "node_modules", ".bin", "vitest"));
|
|
15919
|
+
const hasBats = existsSync26(join28(projectRoot, "tests"));
|
|
15263
15920
|
if (!hasVitest && !hasBats) {
|
|
15264
15921
|
return {
|
|
15265
15922
|
ran: false,
|
|
@@ -15305,14 +15962,14 @@ function coreTestRun(params, projectRoot) {
|
|
|
15305
15962
|
}
|
|
15306
15963
|
}
|
|
15307
15964
|
function coreTestCoverage(projectRoot) {
|
|
15308
|
-
const coveragePath =
|
|
15309
|
-
if (!
|
|
15965
|
+
const coveragePath = join28(projectRoot, "coverage", "coverage-summary.json");
|
|
15966
|
+
if (!existsSync26(coveragePath)) {
|
|
15310
15967
|
return {
|
|
15311
15968
|
available: false,
|
|
15312
15969
|
message: "No coverage data found. Run tests with coverage first."
|
|
15313
15970
|
};
|
|
15314
15971
|
}
|
|
15315
|
-
const coverageData =
|
|
15972
|
+
const coverageData = readJsonFile2(coveragePath);
|
|
15316
15973
|
if (!coverageData) {
|
|
15317
15974
|
throw new Error("Failed to read coverage data");
|
|
15318
15975
|
}
|
|
@@ -15444,7 +16101,7 @@ function validateTestCoverage(projectRoot) {
|
|
|
15444
16101
|
init_platform();
|
|
15445
16102
|
init_paths();
|
|
15446
16103
|
init_data_accessor();
|
|
15447
|
-
import { readFileSync as
|
|
16104
|
+
import { readFileSync as readFileSync21, existsSync as existsSync29 } from "node:fs";
|
|
15448
16105
|
|
|
15449
16106
|
// src/core/orchestration/index.ts
|
|
15450
16107
|
init_json();
|
|
@@ -15573,7 +16230,7 @@ async function analyzeEpic(epicId, cwd, accessor) {
|
|
|
15573
16230
|
completedTasks
|
|
15574
16231
|
};
|
|
15575
16232
|
}
|
|
15576
|
-
async function
|
|
16233
|
+
async function getReadyTasks2(epicId, cwd, accessor) {
|
|
15577
16234
|
const data = accessor ? await accessor.loadTaskFile() : await readJsonRequired(getTaskPath(cwd));
|
|
15578
16235
|
const childTasks = data.tasks.filter((t) => t.parentId === epicId);
|
|
15579
16236
|
const completedIds = new Set(
|
|
@@ -15593,7 +16250,7 @@ async function getReadyTasks(epicId, cwd, accessor) {
|
|
|
15593
16250
|
});
|
|
15594
16251
|
}
|
|
15595
16252
|
async function getNextTask(epicId, cwd, accessor) {
|
|
15596
|
-
const readyTasks = await
|
|
16253
|
+
const readyTasks = await getReadyTasks2(epicId, cwd, accessor);
|
|
15597
16254
|
const ready = readyTasks.filter((t) => t.ready);
|
|
15598
16255
|
if (ready.length === 0) return null;
|
|
15599
16256
|
return ready[0];
|
|
@@ -15954,27 +16611,27 @@ async function getUnblockOpportunities(cwd, accessor) {
|
|
|
15954
16611
|
// src/core/orchestration/parallel.ts
|
|
15955
16612
|
init_errors();
|
|
15956
16613
|
init_exit_codes();
|
|
15957
|
-
import { readFileSync as
|
|
15958
|
-
import { join as
|
|
16614
|
+
import { readFileSync as readFileSync19, writeFileSync as writeFileSync6, existsSync as existsSync27, mkdirSync as mkdirSync10 } from "node:fs";
|
|
16615
|
+
import { join as join29, dirname as dirname9 } from "node:path";
|
|
15959
16616
|
init_json();
|
|
15960
16617
|
init_paths();
|
|
15961
16618
|
function getParallelStatePath(projectRoot) {
|
|
15962
|
-
return
|
|
16619
|
+
return join29(projectRoot, ".cleo", "parallel-state.json");
|
|
15963
16620
|
}
|
|
15964
16621
|
function readParallelState(projectRoot) {
|
|
15965
16622
|
const statePath = getParallelStatePath(projectRoot);
|
|
15966
|
-
if (!
|
|
16623
|
+
if (!existsSync27(statePath)) return { active: false };
|
|
15967
16624
|
try {
|
|
15968
|
-
return JSON.parse(
|
|
16625
|
+
return JSON.parse(readFileSync19(statePath, "utf-8"));
|
|
15969
16626
|
} catch {
|
|
15970
16627
|
return { active: false };
|
|
15971
16628
|
}
|
|
15972
16629
|
}
|
|
15973
16630
|
function writeParallelState(state, projectRoot) {
|
|
15974
16631
|
const statePath = getParallelStatePath(projectRoot);
|
|
15975
|
-
const dir =
|
|
15976
|
-
if (!
|
|
15977
|
-
|
|
16632
|
+
const dir = dirname9(statePath);
|
|
16633
|
+
if (!existsSync27(dir)) mkdirSync10(dir, { recursive: true });
|
|
16634
|
+
writeFileSync6(statePath, JSON.stringify(state, null, 2), "utf-8");
|
|
15978
16635
|
}
|
|
15979
16636
|
async function startParallelExecution(epicId, wave, cwd, accessor) {
|
|
15980
16637
|
const projectRoot = cwd ?? process.cwd();
|
|
@@ -16109,8 +16766,8 @@ async function validateSpawnReadiness(taskId, cwd, accessor) {
|
|
|
16109
16766
|
// src/core/orchestration/bootstrap.ts
|
|
16110
16767
|
init_json();
|
|
16111
16768
|
init_paths();
|
|
16112
|
-
import { readFileSync as
|
|
16113
|
-
import { join as
|
|
16769
|
+
import { readFileSync as readFileSync20, existsSync as existsSync28 } from "node:fs";
|
|
16770
|
+
import { join as join30 } from "node:path";
|
|
16114
16771
|
async function buildBrainState(projectRoot, opts, accessor) {
|
|
16115
16772
|
const speed = opts?.speed || "fast";
|
|
16116
16773
|
const brain = {
|
|
@@ -16122,8 +16779,8 @@ async function buildBrainState(projectRoot, opts, accessor) {
|
|
|
16122
16779
|
};
|
|
16123
16780
|
try {
|
|
16124
16781
|
const sessionsPath = getSessionsPath(projectRoot);
|
|
16125
|
-
if (
|
|
16126
|
-
const sessionsData = JSON.parse(
|
|
16782
|
+
if (existsSync28(sessionsPath)) {
|
|
16783
|
+
const sessionsData = JSON.parse(readFileSync20(sessionsPath, "utf-8"));
|
|
16127
16784
|
const activeSession = (sessionsData.sessions ?? []).find(
|
|
16128
16785
|
(s) => s.status === "active"
|
|
16129
16786
|
);
|
|
@@ -16178,9 +16835,9 @@ async function buildBrainState(projectRoot, opts, accessor) {
|
|
|
16178
16835
|
blockedBy: b.depends || []
|
|
16179
16836
|
}));
|
|
16180
16837
|
try {
|
|
16181
|
-
const decisionLogPath =
|
|
16182
|
-
if (
|
|
16183
|
-
const content =
|
|
16838
|
+
const decisionLogPath = join30(projectRoot, ".cleo", "decision-log.jsonl");
|
|
16839
|
+
if (existsSync28(decisionLogPath)) {
|
|
16840
|
+
const content = readFileSync20(decisionLogPath, "utf-8").trim();
|
|
16184
16841
|
if (content) {
|
|
16185
16842
|
const entries = content.split("\n").filter((l) => l.trim()).map((l) => {
|
|
16186
16843
|
try {
|
|
@@ -16200,9 +16857,9 @@ async function buildBrainState(projectRoot, opts, accessor) {
|
|
|
16200
16857
|
} catch {
|
|
16201
16858
|
}
|
|
16202
16859
|
try {
|
|
16203
|
-
const contextStatePath =
|
|
16204
|
-
if (
|
|
16205
|
-
const state = JSON.parse(
|
|
16860
|
+
const contextStatePath = join30(projectRoot, ".cleo", ".context-state.json");
|
|
16861
|
+
if (existsSync28(contextStatePath)) {
|
|
16862
|
+
const state = JSON.parse(readFileSync20(contextStatePath, "utf-8"));
|
|
16206
16863
|
const percentage = state.contextWindow?.percentage ?? 0;
|
|
16207
16864
|
const factors = [];
|
|
16208
16865
|
if (percentage > 80) factors.push("high_context_usage");
|
|
@@ -16351,7 +17008,7 @@ async function orchestrateReady(epicId, projectRoot) {
|
|
|
16351
17008
|
try {
|
|
16352
17009
|
const root = projectRoot || resolveProjectRoot();
|
|
16353
17010
|
const accessor = await getAccessor(root);
|
|
16354
|
-
const readyTasks = await
|
|
17011
|
+
const readyTasks = await getReadyTasks2(epicId, root, accessor);
|
|
16355
17012
|
const ready = readyTasks.filter((t) => t.ready);
|
|
16356
17013
|
return {
|
|
16357
17014
|
success: true,
|
|
@@ -16390,7 +17047,7 @@ async function orchestrateNext(epicId, projectRoot) {
|
|
|
16390
17047
|
}
|
|
16391
17048
|
};
|
|
16392
17049
|
}
|
|
16393
|
-
const readyTasks = await
|
|
17050
|
+
const readyTasks = await getReadyTasks2(epicId, root, accessor);
|
|
16394
17051
|
const ready = readyTasks.filter((t) => t.ready);
|
|
16395
17052
|
return {
|
|
16396
17053
|
success: true,
|
|
@@ -16430,11 +17087,11 @@ async function orchestrateContext(epicId, projectRoot) {
|
|
|
16430
17087
|
}
|
|
16431
17088
|
const estimatedTokens = taskCount * 100;
|
|
16432
17089
|
const manifestPath = getManifestPath(root);
|
|
16433
|
-
let
|
|
16434
|
-
if (
|
|
17090
|
+
let manifestEntries2 = 0;
|
|
17091
|
+
if (existsSync29(manifestPath)) {
|
|
16435
17092
|
try {
|
|
16436
|
-
const content =
|
|
16437
|
-
|
|
17093
|
+
const content = readFileSync21(manifestPath, "utf-8");
|
|
17094
|
+
manifestEntries2 = content.split("\n").filter((l) => l.trim()).length;
|
|
16438
17095
|
} catch {
|
|
16439
17096
|
}
|
|
16440
17097
|
}
|
|
@@ -16443,7 +17100,7 @@ async function orchestrateContext(epicId, projectRoot) {
|
|
|
16443
17100
|
data: {
|
|
16444
17101
|
epicId: epicId || null,
|
|
16445
17102
|
taskCount,
|
|
16446
|
-
manifestEntries,
|
|
17103
|
+
manifestEntries: manifestEntries2,
|
|
16447
17104
|
estimatedTokens,
|
|
16448
17105
|
recommendation: estimatedTokens > 5e3 ? "Consider using manifest summaries instead of full task details" : "Context usage is within recommended limits",
|
|
16449
17106
|
limits: {
|
|
@@ -16470,7 +17127,7 @@ async function orchestrateValidate(taskId, projectRoot) {
|
|
|
16470
17127
|
return engineError("E_VALIDATION", err.message);
|
|
16471
17128
|
}
|
|
16472
17129
|
}
|
|
16473
|
-
async function orchestrateSpawn(taskId, protocolType, projectRoot) {
|
|
17130
|
+
async function orchestrateSpawn(taskId, protocolType, projectRoot, tier) {
|
|
16474
17131
|
if (!taskId) {
|
|
16475
17132
|
return engineError("E_INVALID_INPUT", "taskId is required");
|
|
16476
17133
|
}
|
|
@@ -16491,9 +17148,11 @@ async function orchestrateSpawn(taskId, protocolType, projectRoot) {
|
|
|
16491
17148
|
spawnContext: {
|
|
16492
17149
|
taskId: spawnContext.taskId,
|
|
16493
17150
|
protocol: spawnContext.protocol,
|
|
16494
|
-
protocolType: protocolType || spawnContext.protocol
|
|
17151
|
+
protocolType: protocolType || spawnContext.protocol,
|
|
17152
|
+
tier: tier ?? null
|
|
16495
17153
|
},
|
|
16496
17154
|
protocolType: protocolType || spawnContext.protocol,
|
|
17155
|
+
tier: tier ?? null,
|
|
16497
17156
|
tokenResolution: spawnContext.tokenResolution
|
|
16498
17157
|
}
|
|
16499
17158
|
};
|
|
@@ -16516,7 +17175,7 @@ async function orchestrateStartup(epicId, projectRoot) {
|
|
|
16516
17175
|
}
|
|
16517
17176
|
const children = tasks2.filter((t) => t.parentId === epicId);
|
|
16518
17177
|
const waves = computeWaves(children);
|
|
16519
|
-
const readyTasks = await
|
|
17178
|
+
const readyTasks = await getReadyTasks2(epicId, root, accessor);
|
|
16520
17179
|
const ready = readyTasks.filter((t) => t.ready);
|
|
16521
17180
|
return {
|
|
16522
17181
|
success: true,
|
|
@@ -16652,8 +17311,8 @@ async function orchestrateCheck(projectRoot) {
|
|
|
16652
17311
|
|
|
16653
17312
|
// src/core/memory/engine-compat.ts
|
|
16654
17313
|
init_paths();
|
|
16655
|
-
import { readFileSync as
|
|
16656
|
-
import { resolve as
|
|
17314
|
+
import { readFileSync as readFileSync24, writeFileSync as writeFileSync9, appendFileSync as appendFileSync6, existsSync as existsSync32, mkdirSync as mkdirSync13 } from "node:fs";
|
|
17315
|
+
import { resolve as resolve7, dirname as dirname10 } from "node:path";
|
|
16657
17316
|
|
|
16658
17317
|
// src/core/memory/index.ts
|
|
16659
17318
|
init_json();
|
|
@@ -16695,25 +17354,25 @@ function filterManifestEntries(entries, filter) {
|
|
|
16695
17354
|
|
|
16696
17355
|
// src/core/memory/patterns.ts
|
|
16697
17356
|
import { randomBytes as randomBytes7 } from "node:crypto";
|
|
16698
|
-
import { readFileSync as
|
|
16699
|
-
import { join as
|
|
17357
|
+
import { readFileSync as readFileSync22, writeFileSync as writeFileSync7, appendFileSync as appendFileSync4, mkdirSync as mkdirSync11, existsSync as existsSync30 } from "node:fs";
|
|
17358
|
+
import { join as join31 } from "node:path";
|
|
16700
17359
|
function getMemoryDir(projectRoot) {
|
|
16701
|
-
const memDir =
|
|
16702
|
-
if (!
|
|
16703
|
-
|
|
17360
|
+
const memDir = join31(projectRoot, ".cleo", "memory");
|
|
17361
|
+
if (!existsSync30(memDir)) {
|
|
17362
|
+
mkdirSync11(memDir, { recursive: true });
|
|
16704
17363
|
}
|
|
16705
17364
|
return memDir;
|
|
16706
17365
|
}
|
|
16707
17366
|
function getPatternsPath(projectRoot) {
|
|
16708
|
-
return
|
|
17367
|
+
return join31(getMemoryDir(projectRoot), "patterns.jsonl");
|
|
16709
17368
|
}
|
|
16710
17369
|
function generatePatternId() {
|
|
16711
17370
|
return `P${randomBytes7(4).toString("hex")}`;
|
|
16712
17371
|
}
|
|
16713
17372
|
function readPatterns(projectRoot) {
|
|
16714
17373
|
const path = getPatternsPath(projectRoot);
|
|
16715
|
-
if (!
|
|
16716
|
-
const content =
|
|
17374
|
+
if (!existsSync30(path)) return [];
|
|
17375
|
+
const content = readFileSync22(path, "utf-8").trim();
|
|
16717
17376
|
if (!content) return [];
|
|
16718
17377
|
const entries = [];
|
|
16719
17378
|
for (const line of content.split("\n")) {
|
|
@@ -16750,7 +17409,7 @@ function storePattern(projectRoot, params) {
|
|
|
16750
17409
|
}
|
|
16751
17410
|
const path2 = getPatternsPath(projectRoot);
|
|
16752
17411
|
const updated = existing.map((e) => JSON.stringify(e)).join("\n") + "\n";
|
|
16753
|
-
|
|
17412
|
+
writeFileSync7(path2, updated, "utf-8");
|
|
16754
17413
|
return duplicate;
|
|
16755
17414
|
}
|
|
16756
17415
|
const entry = {
|
|
@@ -16822,25 +17481,25 @@ function patternStats(projectRoot) {
|
|
|
16822
17481
|
|
|
16823
17482
|
// src/core/memory/learnings.ts
|
|
16824
17483
|
import { randomBytes as randomBytes8 } from "node:crypto";
|
|
16825
|
-
import { readFileSync as
|
|
16826
|
-
import { join as
|
|
17484
|
+
import { readFileSync as readFileSync23, writeFileSync as writeFileSync8, appendFileSync as appendFileSync5, mkdirSync as mkdirSync12, existsSync as existsSync31 } from "node:fs";
|
|
17485
|
+
import { join as join32 } from "node:path";
|
|
16827
17486
|
function getMemoryDir2(projectRoot) {
|
|
16828
|
-
const memDir =
|
|
16829
|
-
if (!
|
|
16830
|
-
|
|
17487
|
+
const memDir = join32(projectRoot, ".cleo", "memory");
|
|
17488
|
+
if (!existsSync31(memDir)) {
|
|
17489
|
+
mkdirSync12(memDir, { recursive: true });
|
|
16831
17490
|
}
|
|
16832
17491
|
return memDir;
|
|
16833
17492
|
}
|
|
16834
17493
|
function getLearningsPath(projectRoot) {
|
|
16835
|
-
return
|
|
17494
|
+
return join32(getMemoryDir2(projectRoot), "learnings.jsonl");
|
|
16836
17495
|
}
|
|
16837
17496
|
function generateLearningId() {
|
|
16838
17497
|
return `L${randomBytes8(4).toString("hex")}`;
|
|
16839
17498
|
}
|
|
16840
17499
|
function readLearnings(projectRoot) {
|
|
16841
17500
|
const path = getLearningsPath(projectRoot);
|
|
16842
|
-
if (!
|
|
16843
|
-
const content =
|
|
17501
|
+
if (!existsSync31(path)) return [];
|
|
17502
|
+
const content = readFileSync23(path, "utf-8").trim();
|
|
16844
17503
|
if (!content) return [];
|
|
16845
17504
|
const entries = [];
|
|
16846
17505
|
for (const line of content.split("\n")) {
|
|
@@ -16879,7 +17538,7 @@ function storeLearning(projectRoot, params) {
|
|
|
16879
17538
|
}
|
|
16880
17539
|
const path2 = getLearningsPath(projectRoot);
|
|
16881
17540
|
const updated = existing.map((e) => JSON.stringify(e)).join("\n") + "\n";
|
|
16882
|
-
|
|
17541
|
+
writeFileSync8(path2, updated, "utf-8");
|
|
16883
17542
|
return duplicate;
|
|
16884
17543
|
}
|
|
16885
17544
|
const entry = {
|
|
@@ -16954,7 +17613,7 @@ function resolveRoot(projectRoot) {
|
|
|
16954
17613
|
function readManifestEntries(projectRoot) {
|
|
16955
17614
|
const manifestPath = getManifestPath2(projectRoot);
|
|
16956
17615
|
try {
|
|
16957
|
-
const content =
|
|
17616
|
+
const content = readFileSync24(manifestPath, "utf-8");
|
|
16958
17617
|
const entries = [];
|
|
16959
17618
|
const lines = content.split("\n");
|
|
16960
17619
|
for (const line of lines) {
|
|
@@ -16989,9 +17648,9 @@ function memoryShow(researchId, projectRoot) {
|
|
|
16989
17648
|
const root = resolveRoot(projectRoot);
|
|
16990
17649
|
let fileContent = null;
|
|
16991
17650
|
try {
|
|
16992
|
-
const filePath =
|
|
16993
|
-
if (
|
|
16994
|
-
fileContent =
|
|
17651
|
+
const filePath = resolve7(root, entry.file);
|
|
17652
|
+
if (existsSync32(filePath)) {
|
|
17653
|
+
fileContent = readFileSync24(filePath, "utf-8");
|
|
16995
17654
|
}
|
|
16996
17655
|
} catch {
|
|
16997
17656
|
}
|
|
@@ -17119,7 +17778,7 @@ function memoryLink(taskId, researchId, notes, projectRoot) {
|
|
|
17119
17778
|
}
|
|
17120
17779
|
entry.linked_tasks.push(taskId);
|
|
17121
17780
|
const content = entries.map((e) => JSON.stringify(e)).join("\n") + "\n";
|
|
17122
|
-
|
|
17781
|
+
writeFileSync9(manifestPath, content, "utf-8");
|
|
17123
17782
|
return { success: true, data: { taskId, researchId, linked: true, notes: notes || null } };
|
|
17124
17783
|
}
|
|
17125
17784
|
function memoryManifestAppend(entry, projectRoot) {
|
|
@@ -17139,9 +17798,9 @@ function memoryManifestAppend(entry, projectRoot) {
|
|
|
17139
17798
|
return { success: false, error: { code: "E_VALIDATION_FAILED", message: `Invalid manifest entry: ${errors.join(", ")}` } };
|
|
17140
17799
|
}
|
|
17141
17800
|
const manifestPath = getManifestPath2(projectRoot);
|
|
17142
|
-
const dir =
|
|
17143
|
-
if (!
|
|
17144
|
-
|
|
17801
|
+
const dir = dirname10(manifestPath);
|
|
17802
|
+
if (!existsSync32(dir)) {
|
|
17803
|
+
mkdirSync13(dir, { recursive: true });
|
|
17145
17804
|
}
|
|
17146
17805
|
const serialized = JSON.stringify(entry);
|
|
17147
17806
|
appendFileSync6(manifestPath, serialized + "\n", "utf-8");
|
|
@@ -17160,14 +17819,14 @@ function memoryManifestArchive(beforeDate, projectRoot) {
|
|
|
17160
17819
|
if (toArchive.length === 0) {
|
|
17161
17820
|
return { success: true, data: { archived: 0, remaining: entries.length, message: "No entries found before the specified date" } };
|
|
17162
17821
|
}
|
|
17163
|
-
const archiveDir =
|
|
17164
|
-
if (!
|
|
17165
|
-
|
|
17822
|
+
const archiveDir = dirname10(archivePath);
|
|
17823
|
+
if (!existsSync32(archiveDir)) {
|
|
17824
|
+
mkdirSync13(archiveDir, { recursive: true });
|
|
17166
17825
|
}
|
|
17167
17826
|
const archiveContent = toArchive.map((e) => JSON.stringify(e)).join("\n") + "\n";
|
|
17168
17827
|
appendFileSync6(archivePath, archiveContent, "utf-8");
|
|
17169
17828
|
const remainingContent = toKeep.length > 0 ? toKeep.map((e) => JSON.stringify(e)).join("\n") + "\n" : "";
|
|
17170
|
-
|
|
17829
|
+
writeFileSync9(manifestPath, remainingContent, "utf-8");
|
|
17171
17830
|
return { success: true, data: { archived: toArchive.length, remaining: toKeep.length, archiveFile: getManifestArchivePath() } };
|
|
17172
17831
|
}
|
|
17173
17832
|
function memoryContradictions(projectRoot, params) {
|
|
@@ -17249,16 +17908,16 @@ function memoryInject(protocolType, params, projectRoot) {
|
|
|
17249
17908
|
}
|
|
17250
17909
|
const root = resolveRoot(projectRoot);
|
|
17251
17910
|
const protocolLocations = [
|
|
17252
|
-
|
|
17253
|
-
|
|
17254
|
-
|
|
17911
|
+
resolve7(root, "protocols", `${protocolType}.md`),
|
|
17912
|
+
resolve7(root, "skills", "_shared", `${protocolType}.md`),
|
|
17913
|
+
resolve7(root, "agents", "cleo-subagent", "protocols", `${protocolType}.md`)
|
|
17255
17914
|
];
|
|
17256
17915
|
let protocolContent = null;
|
|
17257
17916
|
let protocolPath = null;
|
|
17258
17917
|
for (const loc of protocolLocations) {
|
|
17259
|
-
if (
|
|
17918
|
+
if (existsSync32(loc)) {
|
|
17260
17919
|
try {
|
|
17261
|
-
protocolContent =
|
|
17920
|
+
protocolContent = readFileSync24(loc, "utf-8");
|
|
17262
17921
|
protocolPath = loc.replace(root + "/", "");
|
|
17263
17922
|
break;
|
|
17264
17923
|
} catch {
|
|
@@ -17347,11 +18006,11 @@ init_data_accessor();
|
|
|
17347
18006
|
// src/core/release/release-manifest.ts
|
|
17348
18007
|
init_json();
|
|
17349
18008
|
init_paths();
|
|
17350
|
-
import { existsSync as
|
|
18009
|
+
import { existsSync as existsSync33, mkdirSync as mkdirSync14 } from "node:fs";
|
|
17351
18010
|
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
17352
|
-
import { dirname as
|
|
18011
|
+
import { dirname as dirname11, join as join33 } from "node:path";
|
|
17353
18012
|
function getReleasesPath(cwd) {
|
|
17354
|
-
return
|
|
18013
|
+
return join33(getCleoDirAbsolute(cwd), "releases.json");
|
|
17355
18014
|
}
|
|
17356
18015
|
async function readReleases(cwd) {
|
|
17357
18016
|
const data = await readJson(getReleasesPath(cwd));
|
|
@@ -17359,9 +18018,9 @@ async function readReleases(cwd) {
|
|
|
17359
18018
|
}
|
|
17360
18019
|
async function writeReleases(index2, cwd) {
|
|
17361
18020
|
const releasesPath = getReleasesPath(cwd);
|
|
17362
|
-
const dir =
|
|
17363
|
-
if (!
|
|
17364
|
-
|
|
18021
|
+
const dir = dirname11(releasesPath);
|
|
18022
|
+
if (!existsSync33(dir)) {
|
|
18023
|
+
mkdirSync14(dir, { recursive: true });
|
|
17365
18024
|
}
|
|
17366
18025
|
await saveJson(releasesPath, index2);
|
|
17367
18026
|
}
|
|
@@ -17628,7 +18287,7 @@ async function rollbackRelease(version, reason, cwd) {
|
|
|
17628
18287
|
};
|
|
17629
18288
|
}
|
|
17630
18289
|
async function readPushPolicy(cwd) {
|
|
17631
|
-
const configPath =
|
|
18290
|
+
const configPath = join33(getCleoDirAbsolute(cwd), "config.json");
|
|
17632
18291
|
const config = await readJson(configPath);
|
|
17633
18292
|
if (!config) return void 0;
|
|
17634
18293
|
const release2 = config.release;
|
|
@@ -17712,8 +18371,8 @@ async function hasManifestEntry(version, projectRoot) {
|
|
|
17712
18371
|
}
|
|
17713
18372
|
async function loadTasks2(projectRoot) {
|
|
17714
18373
|
if (projectRoot) {
|
|
17715
|
-
const taskPath =
|
|
17716
|
-
const taskData =
|
|
18374
|
+
const taskPath = getDataPath(projectRoot, "todo.json");
|
|
18375
|
+
const taskData = readJsonFile(taskPath);
|
|
17717
18376
|
return taskData?.tasks ?? [];
|
|
17718
18377
|
}
|
|
17719
18378
|
try {
|
|
@@ -17722,8 +18381,8 @@ async function loadTasks2(projectRoot) {
|
|
|
17722
18381
|
return taskFile?.tasks ?? [];
|
|
17723
18382
|
} catch {
|
|
17724
18383
|
const root = resolveProjectRoot();
|
|
17725
|
-
const taskPath =
|
|
17726
|
-
const taskData =
|
|
18384
|
+
const taskPath = getDataPath(root, "todo.json");
|
|
18385
|
+
const taskData = readJsonFile(taskPath);
|
|
17727
18386
|
return taskData?.tasks ?? [];
|
|
17728
18387
|
}
|
|
17729
18388
|
}
|
|
@@ -17843,8 +18502,8 @@ async function releasePush(version, remote, projectRoot, opts) {
|
|
|
17843
18502
|
|
|
17844
18503
|
// src/dispatch/engines/template-parser.ts
|
|
17845
18504
|
init_platform();
|
|
17846
|
-
import { readFileSync as
|
|
17847
|
-
import { join as
|
|
18505
|
+
import { readFileSync as readFileSync25, readdirSync as readdirSync9, existsSync as existsSync34 } from "fs";
|
|
18506
|
+
import { join as join34 } from "path";
|
|
17848
18507
|
import { parse as parseYaml } from "yaml";
|
|
17849
18508
|
var SUFFIX_PATTERNS = ["_report", "_request", "_question"];
|
|
17850
18509
|
function deriveSubcommand(filename) {
|
|
@@ -17859,8 +18518,8 @@ function deriveSubcommand(filename) {
|
|
|
17859
18518
|
return firstWord.toLowerCase();
|
|
17860
18519
|
}
|
|
17861
18520
|
function parseTemplateFile(templateDir, filename) {
|
|
17862
|
-
const filePath =
|
|
17863
|
-
const raw =
|
|
18521
|
+
const filePath = join34(templateDir, filename);
|
|
18522
|
+
const raw = readFileSync25(filePath, "utf-8");
|
|
17864
18523
|
const parsed = parseYaml(raw);
|
|
17865
18524
|
const name = typeof parsed.name === "string" ? parsed.name : filename;
|
|
17866
18525
|
const titlePrefix = typeof parsed.title === "string" ? parsed.title : "";
|
|
@@ -17909,8 +18568,8 @@ function parseTemplateFile(templateDir, filename) {
|
|
|
17909
18568
|
};
|
|
17910
18569
|
}
|
|
17911
18570
|
function parseIssueTemplates(projectRoot) {
|
|
17912
|
-
const templateDir =
|
|
17913
|
-
if (!
|
|
18571
|
+
const templateDir = join34(projectRoot, ".github", "ISSUE_TEMPLATE");
|
|
18572
|
+
if (!existsSync34(templateDir)) {
|
|
17914
18573
|
return engineError("E_NOT_FOUND", `Issue template directory not found: ${templateDir}`);
|
|
17915
18574
|
}
|
|
17916
18575
|
let files;
|
|
@@ -17962,7 +18621,7 @@ async function generateTemplateConfig(projectRoot) {
|
|
|
17962
18621
|
if (!result.success || !result.data) {
|
|
17963
18622
|
return result.error ? { success: false, error: result.error } : engineError("E_PARSE_ERROR", "Failed to parse issue templates");
|
|
17964
18623
|
}
|
|
17965
|
-
const outputPath =
|
|
18624
|
+
const outputPath = getDataPath(projectRoot, "issue-templates.json");
|
|
17966
18625
|
try {
|
|
17967
18626
|
writeJsonFileAtomic(outputPath, result.data);
|
|
17968
18627
|
} catch (error) {
|
|
@@ -17990,7 +18649,6 @@ function validateLabels2(labels, repoLabels) {
|
|
|
17990
18649
|
}
|
|
17991
18650
|
|
|
17992
18651
|
// src/dispatch/domains/tasks.ts
|
|
17993
|
-
var logger2 = getLogger("domain:tasks");
|
|
17994
18652
|
var TasksHandler = class {
|
|
17995
18653
|
projectRoot;
|
|
17996
18654
|
constructor() {
|
|
@@ -18012,14 +18670,26 @@ var TasksHandler = class {
|
|
|
18012
18670
|
return this.wrapEngineResult(result, "query", "tasks", operation, startTime);
|
|
18013
18671
|
}
|
|
18014
18672
|
case "list": {
|
|
18015
|
-
const result = await taskList(this.projectRoot,
|
|
18673
|
+
const result = await taskList(this.projectRoot, {
|
|
18674
|
+
parent: params?.parent,
|
|
18675
|
+
status: params?.status,
|
|
18676
|
+
limit: params?.limit,
|
|
18677
|
+
compact: params?.compact
|
|
18678
|
+
});
|
|
18016
18679
|
return this.wrapEngineResult(result, "query", "tasks", operation, startTime);
|
|
18017
18680
|
}
|
|
18018
18681
|
case "find": {
|
|
18019
18682
|
const result = await taskFind(
|
|
18020
18683
|
this.projectRoot,
|
|
18021
18684
|
params?.query,
|
|
18022
|
-
params?.limit
|
|
18685
|
+
params?.limit,
|
|
18686
|
+
{
|
|
18687
|
+
id: params?.id,
|
|
18688
|
+
exact: params?.exact,
|
|
18689
|
+
status: params?.status,
|
|
18690
|
+
includeArchive: params?.includeArchive,
|
|
18691
|
+
offset: params?.offset
|
|
18692
|
+
}
|
|
18023
18693
|
);
|
|
18024
18694
|
return this.wrapEngineResult(result, "query", "tasks", operation, startTime);
|
|
18025
18695
|
}
|
|
@@ -18041,12 +18711,22 @@ var TasksHandler = class {
|
|
|
18041
18711
|
return this.wrapEngineResult(result, "query", "tasks", operation, startTime);
|
|
18042
18712
|
}
|
|
18043
18713
|
case "depends": {
|
|
18714
|
+
const action = params?.action;
|
|
18715
|
+
if (action === "overview") {
|
|
18716
|
+
const result2 = await taskDepsOverview(this.projectRoot);
|
|
18717
|
+
return this.wrapEngineResult(result2, "query", "tasks", operation, startTime);
|
|
18718
|
+
}
|
|
18719
|
+
if (action === "cycles") {
|
|
18720
|
+
const result2 = await taskDepsCycles(this.projectRoot);
|
|
18721
|
+
return this.wrapEngineResult(result2, "query", "tasks", operation, startTime);
|
|
18722
|
+
}
|
|
18044
18723
|
const taskId = params?.taskId;
|
|
18045
18724
|
if (!taskId) {
|
|
18046
|
-
return this.errorResponse("query", "tasks", operation, "E_INVALID_INPUT", "taskId is required", startTime);
|
|
18725
|
+
return this.errorResponse("query", "tasks", operation, "E_INVALID_INPUT", "taskId is required (or use action: overview|cycles)", startTime);
|
|
18047
18726
|
}
|
|
18048
18727
|
const direction = params?.direction;
|
|
18049
|
-
const
|
|
18728
|
+
const tree = params?.tree;
|
|
18729
|
+
const result = await taskDepends(this.projectRoot, taskId, direction, tree);
|
|
18050
18730
|
return this.wrapEngineResult(result, "query", "tasks", operation, startTime);
|
|
18051
18731
|
}
|
|
18052
18732
|
case "analyze": {
|
|
@@ -18216,10 +18896,10 @@ var TasksHandler = class {
|
|
|
18216
18896
|
}
|
|
18217
18897
|
case "relates.add": {
|
|
18218
18898
|
const taskId = params?.taskId;
|
|
18219
|
-
const relatedId = params?.relatedId;
|
|
18899
|
+
const relatedId = params?.relatedId ?? params?.targetId;
|
|
18220
18900
|
const type = params?.type;
|
|
18221
18901
|
if (!taskId || !relatedId || !type) {
|
|
18222
|
-
return this.errorResponse("mutate", "tasks", operation, "E_INVALID_INPUT", "taskId, relatedId, and type are required", startTime);
|
|
18902
|
+
return this.errorResponse("mutate", "tasks", operation, "E_INVALID_INPUT", "taskId, relatedId (or targetId), and type are required", startTime);
|
|
18223
18903
|
}
|
|
18224
18904
|
const result = await taskRelatesAdd(
|
|
18225
18905
|
this.projectRoot,
|
|
@@ -18313,7 +18993,7 @@ var TasksHandler = class {
|
|
|
18313
18993
|
}
|
|
18314
18994
|
handleError(gateway, domain, operation, error, startTime) {
|
|
18315
18995
|
const message = error instanceof Error ? error.message : String(error);
|
|
18316
|
-
|
|
18996
|
+
getLogger("domain:tasks").error({ gateway, domain, operation, err: error }, message);
|
|
18317
18997
|
return {
|
|
18318
18998
|
_meta: dispatchMeta(gateway, domain, operation, startTime),
|
|
18319
18999
|
success: false,
|
|
@@ -18351,7 +19031,6 @@ function unbindSession() {
|
|
|
18351
19031
|
}
|
|
18352
19032
|
|
|
18353
19033
|
// src/dispatch/domains/session.ts
|
|
18354
|
-
var logger3 = getLogger("domain:session");
|
|
18355
19034
|
var SessionHandler = class {
|
|
18356
19035
|
projectRoot;
|
|
18357
19036
|
constructor() {
|
|
@@ -18472,7 +19151,7 @@ var SessionHandler = class {
|
|
|
18472
19151
|
gradeMode: params?.grade ?? false
|
|
18473
19152
|
});
|
|
18474
19153
|
} catch {
|
|
18475
|
-
|
|
19154
|
+
getLogger("domain:session").warn({ sessionId: session.id }, "Session context already bound, skipping bindSession");
|
|
18476
19155
|
}
|
|
18477
19156
|
}
|
|
18478
19157
|
return this.wrapEngineResult(result, "mutate", "session", operation, startTime);
|
|
@@ -18583,7 +19262,7 @@ var SessionHandler = class {
|
|
|
18583
19262
|
}
|
|
18584
19263
|
handleError(gateway, domain, operation, error, startTime) {
|
|
18585
19264
|
const message = error instanceof Error ? error.message : String(error);
|
|
18586
|
-
|
|
19265
|
+
getLogger("domain:session").error({ gateway, domain, operation, err: error }, message);
|
|
18587
19266
|
return {
|
|
18588
19267
|
_meta: dispatchMeta(gateway, domain, operation, startTime),
|
|
18589
19268
|
success: false,
|
|
@@ -18595,7 +19274,6 @@ var SessionHandler = class {
|
|
|
18595
19274
|
// src/dispatch/domains/check.ts
|
|
18596
19275
|
init_paths();
|
|
18597
19276
|
init_logger();
|
|
18598
|
-
var logger4 = getLogger("domain:check");
|
|
18599
19277
|
var CheckHandler = class {
|
|
18600
19278
|
projectRoot;
|
|
18601
19279
|
constructor() {
|
|
@@ -18754,7 +19432,7 @@ var CheckHandler = class {
|
|
|
18754
19432
|
}
|
|
18755
19433
|
handleError(gateway, domain, operation, error, startTime) {
|
|
18756
19434
|
const message = error instanceof Error ? error.message : String(error);
|
|
18757
|
-
|
|
19435
|
+
getLogger("domain:check").error({ gateway, domain, operation, err: error }, message);
|
|
18758
19436
|
return {
|
|
18759
19437
|
_meta: dispatchMeta(gateway, domain, operation, startTime),
|
|
18760
19438
|
success: false,
|
|
@@ -18768,8 +19446,8 @@ init_paths();
|
|
|
18768
19446
|
init_logger();
|
|
18769
19447
|
|
|
18770
19448
|
// src/core/adrs/parse.ts
|
|
18771
|
-
import { readFileSync as
|
|
18772
|
-
import { join as
|
|
19449
|
+
import { readFileSync as readFileSync26 } from "node:fs";
|
|
19450
|
+
import { join as join35 } from "node:path";
|
|
18773
19451
|
function extractAdrId(filename) {
|
|
18774
19452
|
const match = filename.match(/^(ADR-\d+)/);
|
|
18775
19453
|
return match ? match[1] : filename.replace(".md", "");
|
|
@@ -18791,8 +19469,8 @@ function extractTitle(content) {
|
|
|
18791
19469
|
return match ? match[1].trim() : "Untitled";
|
|
18792
19470
|
}
|
|
18793
19471
|
function parseAdrFile(filePath, projectRoot) {
|
|
18794
|
-
const absolutePath = filePath.startsWith("/") ? filePath :
|
|
18795
|
-
const content =
|
|
19472
|
+
const absolutePath = filePath.startsWith("/") ? filePath : join35(projectRoot, filePath);
|
|
19473
|
+
const content = readFileSync26(absolutePath, "utf-8");
|
|
18796
19474
|
const filename = filePath.split("/").pop();
|
|
18797
19475
|
return {
|
|
18798
19476
|
id: extractAdrId(filename),
|
|
@@ -18803,27 +19481,27 @@ function parseAdrFile(filePath, projectRoot) {
|
|
|
18803
19481
|
}
|
|
18804
19482
|
|
|
18805
19483
|
// src/core/adrs/validate.ts
|
|
18806
|
-
import { readFileSync as
|
|
18807
|
-
import { join as
|
|
19484
|
+
import { readFileSync as readFileSync27, readdirSync as readdirSync10, existsSync as existsSync35 } from "node:fs";
|
|
19485
|
+
import { join as join36 } from "node:path";
|
|
18808
19486
|
import AjvModule3 from "ajv";
|
|
18809
19487
|
var Ajv3 = AjvModule3.default ?? AjvModule3;
|
|
18810
19488
|
async function validateAllAdrs(projectRoot) {
|
|
18811
|
-
const adrsDir =
|
|
18812
|
-
const schemaPath =
|
|
18813
|
-
if (!
|
|
19489
|
+
const adrsDir = join36(projectRoot, ".cleo", "adrs");
|
|
19490
|
+
const schemaPath = join36(projectRoot, "schemas", "adr-frontmatter.schema.json");
|
|
19491
|
+
if (!existsSync35(schemaPath)) {
|
|
18814
19492
|
return {
|
|
18815
19493
|
valid: false,
|
|
18816
19494
|
errors: [{ file: "schemas/adr-frontmatter.schema.json", field: "schema", message: "Schema file not found" }],
|
|
18817
19495
|
checked: 0
|
|
18818
19496
|
};
|
|
18819
19497
|
}
|
|
18820
|
-
if (!
|
|
19498
|
+
if (!existsSync35(adrsDir)) {
|
|
18821
19499
|
return { valid: true, errors: [], checked: 0 };
|
|
18822
19500
|
}
|
|
18823
|
-
const schema = JSON.parse(
|
|
19501
|
+
const schema = JSON.parse(readFileSync27(schemaPath, "utf-8"));
|
|
18824
19502
|
const ajv = new Ajv3({ allErrors: true });
|
|
18825
19503
|
const validate = ajv.compile(schema);
|
|
18826
|
-
const files = readdirSync10(adrsDir).filter((f) => f.endsWith(".md") && f.startsWith("ADR-")).map((f) =>
|
|
19504
|
+
const files = readdirSync10(adrsDir).filter((f) => f.endsWith(".md") && f.startsWith("ADR-")).map((f) => join36(adrsDir, f));
|
|
18827
19505
|
const errors = [];
|
|
18828
19506
|
for (const filePath of files) {
|
|
18829
19507
|
const record = parseAdrFile(filePath, projectRoot);
|
|
@@ -18844,8 +19522,8 @@ async function validateAllAdrs(projectRoot) {
|
|
|
18844
19522
|
// src/core/adrs/sync.ts
|
|
18845
19523
|
init_sqlite();
|
|
18846
19524
|
init_schema();
|
|
18847
|
-
import { readFileSync as
|
|
18848
|
-
import { join as
|
|
19525
|
+
import { readFileSync as readFileSync28, readdirSync as readdirSync11, writeFileSync as writeFileSync10, existsSync as existsSync36 } from "node:fs";
|
|
19526
|
+
import { join as join37 } from "node:path";
|
|
18849
19527
|
import { eq as eq7 } from "drizzle-orm";
|
|
18850
19528
|
function extractAdrIdFromRef(ref) {
|
|
18851
19529
|
const m = ref.match(/^(ADR-\d+)/);
|
|
@@ -18858,7 +19536,7 @@ function collectAdrFiles(dir) {
|
|
|
18858
19536
|
const results = [];
|
|
18859
19537
|
for (const entry of readdirSync11(dir, { withFileTypes: true })) {
|
|
18860
19538
|
if (entry.isDirectory()) {
|
|
18861
|
-
const sub =
|
|
19539
|
+
const sub = join37(dir, entry.name);
|
|
18862
19540
|
for (const f of readdirSync11(sub)) {
|
|
18863
19541
|
if (f.endsWith(".md") && /^ADR-\d+/.test(f)) {
|
|
18864
19542
|
results.push({ file: f, relPath: `${entry.name}/${f}` });
|
|
@@ -18871,23 +19549,23 @@ function collectAdrFiles(dir) {
|
|
|
18871
19549
|
return results.sort((a, b) => a.file.localeCompare(b.file));
|
|
18872
19550
|
}
|
|
18873
19551
|
async function syncAdrsToDb(projectRoot) {
|
|
18874
|
-
const adrsDir =
|
|
19552
|
+
const adrsDir = join37(projectRoot, ".cleo", "adrs");
|
|
18875
19553
|
const result = { inserted: 0, updated: 0, skipped: 0, errors: [] };
|
|
18876
|
-
if (!
|
|
19554
|
+
if (!existsSync36(adrsDir)) {
|
|
18877
19555
|
return result;
|
|
18878
19556
|
}
|
|
18879
19557
|
const db = await getDb(projectRoot);
|
|
18880
19558
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
18881
19559
|
const allFiles = collectAdrFiles(adrsDir);
|
|
18882
19560
|
const activeFiles = allFiles.filter((f) => !f.relPath.includes("/"));
|
|
18883
|
-
const
|
|
19561
|
+
const manifestEntries2 = [];
|
|
18884
19562
|
for (const { file, relPath } of activeFiles) {
|
|
18885
19563
|
try {
|
|
18886
|
-
const filePath =
|
|
19564
|
+
const filePath = join37(adrsDir, relPath);
|
|
18887
19565
|
const record = parseAdrFile(filePath, projectRoot);
|
|
18888
19566
|
const fm = record.frontmatter;
|
|
18889
19567
|
const dbRelPath = `.cleo/adrs/${relPath}`;
|
|
18890
|
-
const content =
|
|
19568
|
+
const content = readFileSync28(filePath, "utf-8");
|
|
18891
19569
|
const supersedesId = fm.Supersedes ? extractAdrIdFromRef(fm.Supersedes) : null;
|
|
18892
19570
|
const supersededById = fm["Superseded By"] ? extractAdrIdFromRef(fm["Superseded By"]) : null;
|
|
18893
19571
|
const amendsId = fm.Amends ? extractAdrIdFromRef(fm.Amends) : null;
|
|
@@ -18929,7 +19607,7 @@ async function syncAdrsToDb(projectRoot) {
|
|
|
18929
19607
|
}
|
|
18930
19608
|
for (const { relPath } of allFiles) {
|
|
18931
19609
|
try {
|
|
18932
|
-
const filePath =
|
|
19610
|
+
const filePath = join37(adrsDir, relPath);
|
|
18933
19611
|
const record = parseAdrFile(filePath, projectRoot);
|
|
18934
19612
|
const fm = record.frontmatter;
|
|
18935
19613
|
const entry = {
|
|
@@ -18952,28 +19630,28 @@ async function syncAdrsToDb(projectRoot) {
|
|
|
18952
19630
|
if (fm.Summary) entry["summary"] = fm.Summary;
|
|
18953
19631
|
if (fm.Keywords) entry["keywords"] = fm.Keywords.split(",").map((s) => s.trim()).filter(Boolean);
|
|
18954
19632
|
if (fm.Topics) entry["topics"] = fm.Topics.split(",").map((s) => s.trim()).filter(Boolean);
|
|
18955
|
-
|
|
19633
|
+
manifestEntries2.push(entry);
|
|
18956
19634
|
} catch {
|
|
18957
19635
|
}
|
|
18958
19636
|
}
|
|
18959
|
-
|
|
18960
|
-
|
|
18961
|
-
|
|
19637
|
+
writeFileSync10(
|
|
19638
|
+
join37(adrsDir, "MANIFEST.jsonl"),
|
|
19639
|
+
manifestEntries2.map((e) => JSON.stringify(e)).join("\n") + "\n",
|
|
18962
19640
|
"utf-8"
|
|
18963
19641
|
);
|
|
18964
19642
|
return result;
|
|
18965
19643
|
}
|
|
18966
19644
|
|
|
18967
19645
|
// src/core/adrs/list.ts
|
|
18968
|
-
import { readdirSync as readdirSync12, existsSync as
|
|
18969
|
-
import { join as
|
|
19646
|
+
import { readdirSync as readdirSync12, existsSync as existsSync37 } from "node:fs";
|
|
19647
|
+
import { join as join38 } from "node:path";
|
|
18970
19648
|
async function listAdrs(projectRoot, opts) {
|
|
18971
|
-
const adrsDir =
|
|
18972
|
-
if (!
|
|
19649
|
+
const adrsDir = join38(projectRoot, ".cleo", "adrs");
|
|
19650
|
+
if (!existsSync37(adrsDir)) {
|
|
18973
19651
|
return { adrs: [], total: 0 };
|
|
18974
19652
|
}
|
|
18975
19653
|
const files = readdirSync12(adrsDir).filter((f) => f.endsWith(".md") && f.startsWith("ADR-")).sort();
|
|
18976
|
-
const records = files.map((f) => parseAdrFile(
|
|
19654
|
+
const records = files.map((f) => parseAdrFile(join38(adrsDir, f), projectRoot));
|
|
18977
19655
|
const filtered = records.filter((r) => {
|
|
18978
19656
|
if (opts?.status && r.frontmatter.Status !== opts.status) return false;
|
|
18979
19657
|
if (opts?.since && r.frontmatter.Date < opts.since) return false;
|
|
@@ -18992,20 +19670,20 @@ async function listAdrs(projectRoot, opts) {
|
|
|
18992
19670
|
}
|
|
18993
19671
|
|
|
18994
19672
|
// src/core/adrs/show.ts
|
|
18995
|
-
import { existsSync as
|
|
18996
|
-
import { join as
|
|
19673
|
+
import { existsSync as existsSync38, readdirSync as readdirSync13 } from "node:fs";
|
|
19674
|
+
import { join as join39 } from "node:path";
|
|
18997
19675
|
async function showAdr(projectRoot, adrId) {
|
|
18998
|
-
const adrsDir =
|
|
18999
|
-
if (!
|
|
19676
|
+
const adrsDir = join39(projectRoot, ".cleo", "adrs");
|
|
19677
|
+
if (!existsSync38(adrsDir)) return null;
|
|
19000
19678
|
const files = readdirSync13(adrsDir).filter((f) => f.startsWith(adrId) && f.endsWith(".md"));
|
|
19001
19679
|
if (files.length === 0) return null;
|
|
19002
|
-
const filePath =
|
|
19680
|
+
const filePath = join39(adrsDir, files[0]);
|
|
19003
19681
|
return parseAdrFile(filePath, projectRoot);
|
|
19004
19682
|
}
|
|
19005
19683
|
|
|
19006
19684
|
// src/core/adrs/find.ts
|
|
19007
|
-
import { readdirSync as readdirSync14, existsSync as
|
|
19008
|
-
import { join as
|
|
19685
|
+
import { readdirSync as readdirSync14, existsSync as existsSync39 } from "node:fs";
|
|
19686
|
+
import { join as join40 } from "node:path";
|
|
19009
19687
|
function normalise(s) {
|
|
19010
19688
|
return s.toLowerCase().replace(/[^a-z0-9\s]/g, " ").replace(/\s+/g, " ").trim();
|
|
19011
19689
|
}
|
|
@@ -19022,8 +19700,8 @@ function matchedTerms(target, terms) {
|
|
|
19022
19700
|
return terms.filter((term) => t.includes(term));
|
|
19023
19701
|
}
|
|
19024
19702
|
async function findAdrs(projectRoot, query, opts) {
|
|
19025
|
-
const adrsDir =
|
|
19026
|
-
if (!
|
|
19703
|
+
const adrsDir = join40(projectRoot, ".cleo", "adrs");
|
|
19704
|
+
if (!existsSync39(adrsDir)) {
|
|
19027
19705
|
return { adrs: [], query, total: 0 };
|
|
19028
19706
|
}
|
|
19029
19707
|
const files = readdirSync14(adrsDir).filter((f) => f.endsWith(".md") && f.startsWith("ADR-")).sort();
|
|
@@ -19032,7 +19710,7 @@ async function findAdrs(projectRoot, query, opts) {
|
|
|
19032
19710
|
const filterKeywords = opts?.keywords ? parseTags(opts.keywords) : null;
|
|
19033
19711
|
const results = [];
|
|
19034
19712
|
for (const file of files) {
|
|
19035
|
-
const record = parseAdrFile(
|
|
19713
|
+
const record = parseAdrFile(join40(adrsDir, file), projectRoot);
|
|
19036
19714
|
const fm = record.frontmatter;
|
|
19037
19715
|
if (opts?.status && fm.Status !== opts.status) continue;
|
|
19038
19716
|
if (filterTopics && filterTopics.length > 0) {
|
|
@@ -19095,7 +19773,6 @@ init_schema();
|
|
|
19095
19773
|
import { eq as eq8, and as and2 } from "drizzle-orm";
|
|
19096
19774
|
|
|
19097
19775
|
// src/dispatch/domains/admin.ts
|
|
19098
|
-
var logger5 = getLogger("domain:admin");
|
|
19099
19776
|
var AdminHandler = class {
|
|
19100
19777
|
projectRoot;
|
|
19101
19778
|
constructor() {
|
|
@@ -19178,6 +19855,14 @@ var AdminHandler = class {
|
|
|
19178
19855
|
case "help": {
|
|
19179
19856
|
const tier = typeof params?.tier === "number" ? params.tier : 0;
|
|
19180
19857
|
const ops = OPERATIONS.filter((op) => op.tier <= tier);
|
|
19858
|
+
const getCostHint = (domain, op) => {
|
|
19859
|
+
const key = `${domain}.${op}`;
|
|
19860
|
+
const heavyOps = ["tasks.list", "tasks.tree", "admin.log", "admin.stats", "tasks.analyze"];
|
|
19861
|
+
const moderateOps = ["tasks.show", "tasks.blockers", "tasks.depends", "admin.health", "admin.dash", "admin.help"];
|
|
19862
|
+
if (heavyOps.includes(key)) return "heavy";
|
|
19863
|
+
if (moderateOps.includes(key)) return "moderate";
|
|
19864
|
+
return "minimal";
|
|
19865
|
+
};
|
|
19181
19866
|
const tierGuidance = {
|
|
19182
19867
|
0: "Tier 0: Core task and session operations (tasks, session, admin). 80% of use cases.",
|
|
19183
19868
|
1: "Tier 1: + memory/research and check/validate operations. 15% of use cases.",
|
|
@@ -19189,14 +19874,22 @@ var AdminHandler = class {
|
|
|
19189
19874
|
data: {
|
|
19190
19875
|
tier,
|
|
19191
19876
|
operationCount: ops.length,
|
|
19877
|
+
quickStart: tier === 0 ? [
|
|
19878
|
+
"cleo_query tasks.current \u2014 check active task (~100 tokens)",
|
|
19879
|
+
"cleo_query tasks.next \u2014 get suggestion (~300 tokens)",
|
|
19880
|
+
"cleo_query tasks.find {query} \u2014 search tasks (~200 tokens)",
|
|
19881
|
+
"cleo_mutate tasks.start {taskId} \u2014 begin work (~100 tokens)",
|
|
19882
|
+
"cleo_mutate tasks.complete {taskId} \u2014 finish task (~200 tokens)"
|
|
19883
|
+
] : void 0,
|
|
19192
19884
|
operations: ops.map((op) => ({
|
|
19193
19885
|
gateway: op.gateway,
|
|
19194
19886
|
domain: op.domain,
|
|
19195
19887
|
operation: op.operation,
|
|
19196
|
-
description: op.description
|
|
19888
|
+
description: op.description,
|
|
19889
|
+
costHint: getCostHint(op.domain, op.operation)
|
|
19197
19890
|
})),
|
|
19198
19891
|
guidance: tierGuidance[tier] ?? tierGuidance[0],
|
|
19199
|
-
escalation: tier < 2 ? `For more operations:
|
|
19892
|
+
escalation: tier < 2 ? `For more operations: cleo_query({domain:"admin",operation:"help",params:{tier:${tier + 1}}})` : "Full operation set displayed."
|
|
19200
19893
|
}
|
|
19201
19894
|
};
|
|
19202
19895
|
}
|
|
@@ -19447,7 +20140,7 @@ var AdminHandler = class {
|
|
|
19447
20140
|
}
|
|
19448
20141
|
handleError(gateway, domain, operation, error, startTime) {
|
|
19449
20142
|
const message = error instanceof Error ? error.message : String(error);
|
|
19450
|
-
|
|
20143
|
+
getLogger("domain:admin").error({ gateway, domain, operation, err: error }, message);
|
|
19451
20144
|
return {
|
|
19452
20145
|
_meta: dispatchMeta(gateway, domain, operation, startTime),
|
|
19453
20146
|
success: false,
|
|
@@ -19459,7 +20152,6 @@ var AdminHandler = class {
|
|
|
19459
20152
|
// src/dispatch/domains/memory.ts
|
|
19460
20153
|
init_paths();
|
|
19461
20154
|
init_logger();
|
|
19462
|
-
var logger6 = getLogger("domain:memory");
|
|
19463
20155
|
var MemoryHandler = class {
|
|
19464
20156
|
projectRoot;
|
|
19465
20157
|
constructor() {
|
|
@@ -19692,7 +20384,7 @@ var MemoryHandler = class {
|
|
|
19692
20384
|
}
|
|
19693
20385
|
handleError(gateway, domain, operation, error, startTime) {
|
|
19694
20386
|
const message = error instanceof Error ? error.message : String(error);
|
|
19695
|
-
|
|
20387
|
+
getLogger("domain:memory").error({ gateway, domain, operation, err: error }, message);
|
|
19696
20388
|
return {
|
|
19697
20389
|
_meta: dispatchMeta(gateway, domain, operation, startTime),
|
|
19698
20390
|
success: false,
|
|
@@ -19704,7 +20396,6 @@ var MemoryHandler = class {
|
|
|
19704
20396
|
// src/dispatch/domains/orchestrate.ts
|
|
19705
20397
|
init_paths();
|
|
19706
20398
|
init_logger();
|
|
19707
|
-
var logger7 = getLogger("domain:orchestrate");
|
|
19708
20399
|
var OrchestrateHandler = class {
|
|
19709
20400
|
projectRoot;
|
|
19710
20401
|
constructor() {
|
|
@@ -19839,7 +20530,8 @@ var OrchestrateHandler = class {
|
|
|
19839
20530
|
);
|
|
19840
20531
|
}
|
|
19841
20532
|
const protocolType = params?.protocolType;
|
|
19842
|
-
const
|
|
20533
|
+
const tier = params?.tier;
|
|
20534
|
+
const result = await orchestrateSpawn(taskId, protocolType, this.projectRoot, tier);
|
|
19843
20535
|
return this.wrapEngineResult(result, "mutate", operation, startTime);
|
|
19844
20536
|
}
|
|
19845
20537
|
case "validate": {
|
|
@@ -19973,7 +20665,7 @@ var OrchestrateHandler = class {
|
|
|
19973
20665
|
}
|
|
19974
20666
|
handleError(gateway, operation, error, startTime) {
|
|
19975
20667
|
const message = error instanceof Error ? error.message : String(error);
|
|
19976
|
-
|
|
20668
|
+
getLogger("domain:orchestrate").error({ gateway, domain: "orchestrate", operation, err: error }, message);
|
|
19977
20669
|
return this.errorResponse(
|
|
19978
20670
|
gateway,
|
|
19979
20671
|
operation,
|
|
@@ -19987,7 +20679,6 @@ var OrchestrateHandler = class {
|
|
|
19987
20679
|
// src/dispatch/domains/pipeline.ts
|
|
19988
20680
|
init_paths();
|
|
19989
20681
|
init_logger();
|
|
19990
|
-
var logger8 = getLogger("domain:pipeline");
|
|
19991
20682
|
var PipelineHandler = class {
|
|
19992
20683
|
projectRoot;
|
|
19993
20684
|
constructor() {
|
|
@@ -20388,7 +21079,7 @@ var PipelineHandler = class {
|
|
|
20388
21079
|
}
|
|
20389
21080
|
handleError(gateway, operation, error, startTime) {
|
|
20390
21081
|
const message = error instanceof Error ? error.message : String(error);
|
|
20391
|
-
|
|
21082
|
+
getLogger("domain:pipeline").error({ gateway, domain: "pipeline", operation, err: error }, message);
|
|
20392
21083
|
return this.errorResponse(
|
|
20393
21084
|
gateway,
|
|
20394
21085
|
operation,
|
|
@@ -20451,8 +21142,8 @@ import { execFileSync as execFileSync5 } from "node:child_process";
|
|
|
20451
21142
|
|
|
20452
21143
|
// src/core/issue/template-parser.ts
|
|
20453
21144
|
init_paths();
|
|
20454
|
-
import { existsSync as
|
|
20455
|
-
import { join as
|
|
21145
|
+
import { existsSync as existsSync41, readFileSync as readFileSync29, readdirSync as readdirSync15, writeFileSync as writeFileSync11 } from "node:fs";
|
|
21146
|
+
import { join as join42, basename as basename6 } from "node:path";
|
|
20456
21147
|
var TEMPLATE_DIR = ".github/ISSUE_TEMPLATE";
|
|
20457
21148
|
var CACHE_FILE = "issue-templates.json";
|
|
20458
21149
|
var SUBCOMMAND_MAP = {
|
|
@@ -20509,10 +21200,10 @@ function extractYamlArray(content, field) {
|
|
|
20509
21200
|
return items;
|
|
20510
21201
|
}
|
|
20511
21202
|
function parseTemplateFile2(filePath) {
|
|
20512
|
-
if (!
|
|
21203
|
+
if (!existsSync41(filePath)) return null;
|
|
20513
21204
|
try {
|
|
20514
|
-
const content =
|
|
20515
|
-
const fileName =
|
|
21205
|
+
const content = readFileSync29(filePath, "utf-8");
|
|
21206
|
+
const fileName = basename6(filePath);
|
|
20516
21207
|
const stem = fileName.replace(/\.ya?ml$/, "");
|
|
20517
21208
|
const name = extractYamlField(content, "name");
|
|
20518
21209
|
const description = extractYamlField(content, "description");
|
|
@@ -20527,12 +21218,12 @@ function parseTemplateFile2(filePath) {
|
|
|
20527
21218
|
}
|
|
20528
21219
|
function parseIssueTemplates2(projectDir) {
|
|
20529
21220
|
const dir = projectDir ?? getProjectRoot();
|
|
20530
|
-
const templateDir =
|
|
20531
|
-
if (!
|
|
21221
|
+
const templateDir = join42(dir, TEMPLATE_DIR);
|
|
21222
|
+
if (!existsSync41(templateDir)) return [];
|
|
20532
21223
|
const templates = [];
|
|
20533
21224
|
for (const file of readdirSync15(templateDir)) {
|
|
20534
21225
|
if (!file.endsWith(".yml") && !file.endsWith(".yaml")) continue;
|
|
20535
|
-
const template = parseTemplateFile2(
|
|
21226
|
+
const template = parseTemplateFile2(join42(templateDir, file));
|
|
20536
21227
|
if (template) templates.push(template);
|
|
20537
21228
|
}
|
|
20538
21229
|
return templates;
|
|
@@ -20541,10 +21232,10 @@ function getTemplateConfig(cwd) {
|
|
|
20541
21232
|
const projectDir = cwd ?? getProjectRoot();
|
|
20542
21233
|
const liveTemplates = parseIssueTemplates2(projectDir);
|
|
20543
21234
|
if (liveTemplates.length > 0) return liveTemplates;
|
|
20544
|
-
const cachePath =
|
|
20545
|
-
if (
|
|
21235
|
+
const cachePath = join42(getCleoDir(cwd), CACHE_FILE);
|
|
21236
|
+
if (existsSync41(cachePath)) {
|
|
20546
21237
|
try {
|
|
20547
|
-
const cached = JSON.parse(
|
|
21238
|
+
const cached = JSON.parse(readFileSync29(cachePath, "utf-8"));
|
|
20548
21239
|
if (cached.templates?.length > 0) return cached.templates;
|
|
20549
21240
|
} catch {
|
|
20550
21241
|
}
|
|
@@ -20669,7 +21360,6 @@ import {
|
|
|
20669
21360
|
injectAll,
|
|
20670
21361
|
buildInjectionContent
|
|
20671
21362
|
} from "@cleocode/caamp";
|
|
20672
|
-
var logger9 = getLogger("domain:tools");
|
|
20673
21363
|
var ToolsHandler = class {
|
|
20674
21364
|
projectRoot;
|
|
20675
21365
|
constructor() {
|
|
@@ -21304,7 +21994,7 @@ var ToolsHandler = class {
|
|
|
21304
21994
|
}
|
|
21305
21995
|
handleError(gateway, domain, operation, error, startTime) {
|
|
21306
21996
|
const message = error instanceof Error ? error.message : String(error);
|
|
21307
|
-
|
|
21997
|
+
getLogger("domain:tools").error({ gateway, domain, operation, err: error }, message);
|
|
21308
21998
|
return this.errorResponse(
|
|
21309
21999
|
gateway,
|
|
21310
22000
|
domain,
|
|
@@ -21318,11 +22008,10 @@ var ToolsHandler = class {
|
|
|
21318
22008
|
|
|
21319
22009
|
// src/dispatch/domains/nexus.ts
|
|
21320
22010
|
init_logger();
|
|
21321
|
-
var logger10 = getLogger("domain:nexus");
|
|
21322
22011
|
var NexusHandler = class {
|
|
21323
22012
|
async query(operation, _params) {
|
|
21324
22013
|
const startTime = Date.now();
|
|
21325
|
-
|
|
22014
|
+
getLogger("domain:nexus").warn({ operation }, `Nexus domain not yet implemented: ${operation}`);
|
|
21326
22015
|
return {
|
|
21327
22016
|
_meta: dispatchMeta("query", "nexus", operation, startTime),
|
|
21328
22017
|
success: false,
|
|
@@ -21331,7 +22020,7 @@ var NexusHandler = class {
|
|
|
21331
22020
|
}
|
|
21332
22021
|
async mutate(operation, _params) {
|
|
21333
22022
|
const startTime = Date.now();
|
|
21334
|
-
|
|
22023
|
+
getLogger("domain:nexus").warn({ operation }, `Nexus domain not yet implemented: ${operation}`);
|
|
21335
22024
|
return {
|
|
21336
22025
|
_meta: dispatchMeta("mutate", "nexus", operation, startTime),
|
|
21337
22026
|
success: false,
|
|
@@ -21349,8 +22038,8 @@ init_logger();
|
|
|
21349
22038
|
|
|
21350
22039
|
// src/core/sharing/index.ts
|
|
21351
22040
|
import { readFile as readFile7, writeFile as writeFile4 } from "node:fs/promises";
|
|
21352
|
-
import { existsSync as
|
|
21353
|
-
import { join as
|
|
22041
|
+
import { existsSync as existsSync42, readdirSync as readdirSync16, statSync as statSync5 } from "node:fs";
|
|
22042
|
+
import { join as join43, relative } from "node:path";
|
|
21354
22043
|
init_paths();
|
|
21355
22044
|
var GITIGNORE_START = "# CLEO:SHARING:START - Auto-managed by cleo sharing sync";
|
|
21356
22045
|
var GITIGNORE_END = "# CLEO:SHARING:END";
|
|
@@ -21378,7 +22067,7 @@ function collectCleoFiles(cleoDir) {
|
|
|
21378
22067
|
const entries = readdirSync16(dir);
|
|
21379
22068
|
for (const entry of entries) {
|
|
21380
22069
|
if (entry === ".git") continue;
|
|
21381
|
-
const fullPath =
|
|
22070
|
+
const fullPath = join43(dir, entry);
|
|
21382
22071
|
const relPath = relative(cleoDir, fullPath);
|
|
21383
22072
|
try {
|
|
21384
22073
|
const stat2 = statSync5(fullPath);
|
|
@@ -21438,7 +22127,7 @@ function generateGitignoreEntries(sharing) {
|
|
|
21438
22127
|
async function syncGitignore(cwd) {
|
|
21439
22128
|
const config = await loadConfig2(cwd);
|
|
21440
22129
|
const projectRoot = getProjectRoot(cwd);
|
|
21441
|
-
const gitignorePath =
|
|
22130
|
+
const gitignorePath = join43(projectRoot, ".gitignore");
|
|
21442
22131
|
const entries = generateGitignoreEntries(config.sharing);
|
|
21443
22132
|
const managedSection = [
|
|
21444
22133
|
"",
|
|
@@ -21448,7 +22137,7 @@ async function syncGitignore(cwd) {
|
|
|
21448
22137
|
""
|
|
21449
22138
|
].join("\n");
|
|
21450
22139
|
let content = "";
|
|
21451
|
-
if (
|
|
22140
|
+
if (existsSync42(gitignorePath)) {
|
|
21452
22141
|
content = await readFile7(gitignorePath, "utf-8");
|
|
21453
22142
|
}
|
|
21454
22143
|
const startIdx = content.indexOf(GITIGNORE_START);
|
|
@@ -21473,8 +22162,8 @@ init_data_accessor();
|
|
|
21473
22162
|
init_paths();
|
|
21474
22163
|
import { createHash as createHash6 } from "node:crypto";
|
|
21475
22164
|
import { readFile as readFile8, writeFile as writeFile5, mkdir as mkdir7 } from "node:fs/promises";
|
|
21476
|
-
import { existsSync as
|
|
21477
|
-
import { join as
|
|
22165
|
+
import { existsSync as existsSync43 } from "node:fs";
|
|
22166
|
+
import { join as join44, dirname as dirname12 } from "node:path";
|
|
21478
22167
|
var SNAPSHOT_FORMAT_VERSION = "1.0.0";
|
|
21479
22168
|
function toSnapshotTask(task) {
|
|
21480
22169
|
return {
|
|
@@ -21527,8 +22216,8 @@ async function exportSnapshot(cwd) {
|
|
|
21527
22216
|
};
|
|
21528
22217
|
}
|
|
21529
22218
|
async function writeSnapshot(snapshot, outputPath) {
|
|
21530
|
-
const dir =
|
|
21531
|
-
if (!
|
|
22219
|
+
const dir = dirname12(outputPath);
|
|
22220
|
+
if (!existsSync43(dir)) {
|
|
21532
22221
|
await mkdir7(dir, { recursive: true });
|
|
21533
22222
|
}
|
|
21534
22223
|
await writeFile5(outputPath, JSON.stringify(snapshot, null, 2) + "\n");
|
|
@@ -21544,7 +22233,7 @@ async function readSnapshot(inputPath) {
|
|
|
21544
22233
|
function getDefaultSnapshotPath(cwd) {
|
|
21545
22234
|
const cleoDir = getCleoDirAbsolute(cwd);
|
|
21546
22235
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
21547
|
-
return
|
|
22236
|
+
return join44(cleoDir, "snapshots", `snapshot-${timestamp}.json`);
|
|
21548
22237
|
}
|
|
21549
22238
|
async function importSnapshot(snapshot, cwd) {
|
|
21550
22239
|
const accessor = await getAccessor(cwd);
|
|
@@ -21611,10 +22300,10 @@ init_paths();
|
|
|
21611
22300
|
init_git_checkpoint();
|
|
21612
22301
|
import { execFile as execFile6 } from "node:child_process";
|
|
21613
22302
|
import { promisify as promisify6 } from "node:util";
|
|
21614
|
-
import { resolve as
|
|
22303
|
+
import { resolve as resolve8 } from "node:path";
|
|
21615
22304
|
var execFileAsync4 = promisify6(execFile6);
|
|
21616
22305
|
async function cleoGitExec(args, cleoDir) {
|
|
21617
|
-
const abs =
|
|
22306
|
+
const abs = resolve8(cleoDir);
|
|
21618
22307
|
const result = await execFileAsync4("git", args, {
|
|
21619
22308
|
cwd: abs,
|
|
21620
22309
|
env: makeCleoGitEnv(cleoDir),
|
|
@@ -21801,7 +22490,6 @@ async function getSyncStatus(remote = "origin", cwd) {
|
|
|
21801
22490
|
}
|
|
21802
22491
|
|
|
21803
22492
|
// src/dispatch/domains/sharing.ts
|
|
21804
|
-
var logger11 = getLogger("domain:sharing");
|
|
21805
22493
|
var SharingHandler = class {
|
|
21806
22494
|
projectRoot;
|
|
21807
22495
|
constructor() {
|
|
@@ -21986,7 +22674,7 @@ var SharingHandler = class {
|
|
|
21986
22674
|
}
|
|
21987
22675
|
handleError(gateway, operation, error, startTime) {
|
|
21988
22676
|
const message = error instanceof Error ? error.message : String(error);
|
|
21989
|
-
|
|
22677
|
+
getLogger("domain:sharing").error({ gateway, operation, err: error }, message);
|
|
21990
22678
|
return {
|
|
21991
22679
|
_meta: dispatchMeta(gateway, "sharing", operation, startTime),
|
|
21992
22680
|
success: false,
|
|
@@ -22049,7 +22737,7 @@ function createSessionResolver(cliSessionLookup) {
|
|
|
22049
22737
|
// src/dispatch/lib/security.ts
|
|
22050
22738
|
init_schema();
|
|
22051
22739
|
init_status_registry();
|
|
22052
|
-
import { resolve as
|
|
22740
|
+
import { resolve as resolve9, normalize, relative as relative2, isAbsolute } from "path";
|
|
22053
22741
|
var SecurityError = class extends Error {
|
|
22054
22742
|
constructor(message, code = "E_SECURITY_VIOLATION", field) {
|
|
22055
22743
|
super(message);
|
|
@@ -22125,12 +22813,12 @@ function sanitizePath(path, projectRoot) {
|
|
|
22125
22813
|
"path"
|
|
22126
22814
|
);
|
|
22127
22815
|
}
|
|
22128
|
-
const normalizedRoot =
|
|
22816
|
+
const normalizedRoot = resolve9(projectRoot);
|
|
22129
22817
|
let resolvedPath;
|
|
22130
22818
|
if (isAbsolute(trimmedPath)) {
|
|
22131
22819
|
resolvedPath = normalize(trimmedPath);
|
|
22132
22820
|
} else {
|
|
22133
|
-
resolvedPath =
|
|
22821
|
+
resolvedPath = resolve9(normalizedRoot, trimmedPath);
|
|
22134
22822
|
}
|
|
22135
22823
|
const relativePath = relative2(normalizedRoot, resolvedPath);
|
|
22136
22824
|
if (relativePath.startsWith("..") || isAbsolute(relativePath)) {
|
|
@@ -22179,6 +22867,27 @@ function validateEnum(value, allowed, fieldName) {
|
|
|
22179
22867
|
}
|
|
22180
22868
|
var ALL_VALID_STATUSES = [...TASK_STATUSES, ...MANIFEST_STATUSES];
|
|
22181
22869
|
var VALID_PRIORITIES2 = TASK_PRIORITIES;
|
|
22870
|
+
var ARRAY_PARAMS = /* @__PURE__ */ new Set([
|
|
22871
|
+
"labels",
|
|
22872
|
+
"addLabels",
|
|
22873
|
+
"removeLabels",
|
|
22874
|
+
"depends",
|
|
22875
|
+
"addDepends",
|
|
22876
|
+
"removeDepends",
|
|
22877
|
+
"files",
|
|
22878
|
+
"acceptance",
|
|
22879
|
+
"findings",
|
|
22880
|
+
"sources",
|
|
22881
|
+
"tasks",
|
|
22882
|
+
"removeTasks",
|
|
22883
|
+
"taskIds"
|
|
22884
|
+
]);
|
|
22885
|
+
function ensureArray(value, separator = ",") {
|
|
22886
|
+
if (value === void 0 || value === null) return void 0;
|
|
22887
|
+
if (Array.isArray(value)) return value.map((v) => typeof v === "string" ? v.trim() : String(v));
|
|
22888
|
+
if (typeof value === "string") return value.split(separator).map((s) => s.trim()).filter(Boolean);
|
|
22889
|
+
return [String(value)];
|
|
22890
|
+
}
|
|
22182
22891
|
function sanitizeParams(params, projectRoot, context) {
|
|
22183
22892
|
if (!params) {
|
|
22184
22893
|
return params;
|
|
@@ -22238,6 +22947,11 @@ function sanitizeParams(params, projectRoot, context) {
|
|
|
22238
22947
|
continue;
|
|
22239
22948
|
}
|
|
22240
22949
|
}
|
|
22950
|
+
for (const key of Object.keys(sanitized)) {
|
|
22951
|
+
if (ARRAY_PARAMS.has(key) && sanitized[key] !== void 0) {
|
|
22952
|
+
sanitized[key] = ensureArray(sanitized[key]);
|
|
22953
|
+
}
|
|
22954
|
+
}
|
|
22241
22955
|
return sanitized;
|
|
22242
22956
|
}
|
|
22243
22957
|
|
|
@@ -24172,6 +24886,142 @@ function createProtocolEnforcement(strictMode = true) {
|
|
|
24172
24886
|
|
|
24173
24887
|
// src/dispatch/adapters/mcp.ts
|
|
24174
24888
|
init_audit();
|
|
24889
|
+
|
|
24890
|
+
// src/dispatch/lib/projections.ts
|
|
24891
|
+
var PROJECTIONS = {
|
|
24892
|
+
minimal: {
|
|
24893
|
+
allowedDomains: ["tasks", "session", "admin"],
|
|
24894
|
+
excludeFields: ["notes", "history", "metadata._internal", "auditLog"],
|
|
24895
|
+
maxDepth: 2
|
|
24896
|
+
},
|
|
24897
|
+
standard: {
|
|
24898
|
+
allowedDomains: ["tasks", "session", "admin", "memory", "check", "pipeline", "tools", "validate"],
|
|
24899
|
+
excludeFields: ["metadata._internal", "auditLog"],
|
|
24900
|
+
maxDepth: 4
|
|
24901
|
+
},
|
|
24902
|
+
orchestrator: {
|
|
24903
|
+
allowedDomains: [
|
|
24904
|
+
"tasks",
|
|
24905
|
+
"session",
|
|
24906
|
+
"admin",
|
|
24907
|
+
"memory",
|
|
24908
|
+
"check",
|
|
24909
|
+
"pipeline",
|
|
24910
|
+
"orchestrate",
|
|
24911
|
+
"tools",
|
|
24912
|
+
"sharing",
|
|
24913
|
+
"nexus",
|
|
24914
|
+
"validate",
|
|
24915
|
+
"lifecycle",
|
|
24916
|
+
"release",
|
|
24917
|
+
"system"
|
|
24918
|
+
],
|
|
24919
|
+
maxDepth: 8
|
|
24920
|
+
}
|
|
24921
|
+
};
|
|
24922
|
+
var VALID_TIERS = /* @__PURE__ */ new Set(["minimal", "standard", "orchestrator"]);
|
|
24923
|
+
function resolveTier(params, sessionScope) {
|
|
24924
|
+
const mvi = params?._mviTier;
|
|
24925
|
+
if (typeof mvi === "string" && VALID_TIERS.has(mvi)) return mvi;
|
|
24926
|
+
if (sessionScope?.type === "epic") return "orchestrator";
|
|
24927
|
+
return "standard";
|
|
24928
|
+
}
|
|
24929
|
+
|
|
24930
|
+
// src/dispatch/middleware/projection.ts
|
|
24931
|
+
function pruneDepth(data, maxDepth, currentDepth = 0) {
|
|
24932
|
+
if (data === null || data === void 0 || typeof data !== "object") {
|
|
24933
|
+
return data;
|
|
24934
|
+
}
|
|
24935
|
+
if (currentDepth >= maxDepth) {
|
|
24936
|
+
if (Array.isArray(data)) {
|
|
24937
|
+
return `[Array(${data.length})]`;
|
|
24938
|
+
}
|
|
24939
|
+
return "[Object]";
|
|
24940
|
+
}
|
|
24941
|
+
if (Array.isArray(data)) {
|
|
24942
|
+
return data.map((item) => pruneDepth(item, maxDepth, currentDepth + 1));
|
|
24943
|
+
}
|
|
24944
|
+
const result = {};
|
|
24945
|
+
for (const [key, value] of Object.entries(data)) {
|
|
24946
|
+
result[key] = pruneDepth(value, maxDepth, currentDepth + 1);
|
|
24947
|
+
}
|
|
24948
|
+
return result;
|
|
24949
|
+
}
|
|
24950
|
+
function applyProjection(data, config) {
|
|
24951
|
+
if (!data || typeof data !== "object") return data;
|
|
24952
|
+
let result = data;
|
|
24953
|
+
if (config.excludeFields?.length) {
|
|
24954
|
+
const obj = { ...data };
|
|
24955
|
+
for (const field of config.excludeFields) {
|
|
24956
|
+
const parts = field.split(".");
|
|
24957
|
+
if (parts.length === 1) {
|
|
24958
|
+
delete obj[parts[0]];
|
|
24959
|
+
} else {
|
|
24960
|
+
let current = obj;
|
|
24961
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
24962
|
+
const val = current?.[parts[i]];
|
|
24963
|
+
if (val && typeof val === "object") {
|
|
24964
|
+
current[parts[i]] = { ...val };
|
|
24965
|
+
current = current[parts[i]];
|
|
24966
|
+
} else {
|
|
24967
|
+
current = void 0;
|
|
24968
|
+
break;
|
|
24969
|
+
}
|
|
24970
|
+
}
|
|
24971
|
+
if (current) {
|
|
24972
|
+
delete current[parts[parts.length - 1]];
|
|
24973
|
+
}
|
|
24974
|
+
}
|
|
24975
|
+
}
|
|
24976
|
+
result = obj;
|
|
24977
|
+
}
|
|
24978
|
+
if (config.maxDepth !== void 0) {
|
|
24979
|
+
result = pruneDepth(result, config.maxDepth);
|
|
24980
|
+
}
|
|
24981
|
+
return result;
|
|
24982
|
+
}
|
|
24983
|
+
function createProjectionMiddleware() {
|
|
24984
|
+
return async (req, next) => {
|
|
24985
|
+
const session = getBoundSession();
|
|
24986
|
+
const tier = resolveTier(req.params, session?.scope ?? null);
|
|
24987
|
+
const config = PROJECTIONS[tier];
|
|
24988
|
+
if (req.params) {
|
|
24989
|
+
delete req.params["_mviTier"];
|
|
24990
|
+
}
|
|
24991
|
+
if (req.source === "mcp" && req.domain === "tasks" && req.operation === "list") {
|
|
24992
|
+
if (req.params && req.params["compact"] === void 0) {
|
|
24993
|
+
req.params["compact"] = true;
|
|
24994
|
+
}
|
|
24995
|
+
}
|
|
24996
|
+
if (!config.allowedDomains.includes(req.domain)) {
|
|
24997
|
+
return {
|
|
24998
|
+
_meta: {
|
|
24999
|
+
gateway: req.gateway,
|
|
25000
|
+
domain: req.domain,
|
|
25001
|
+
operation: req.operation,
|
|
25002
|
+
version: "1.0.0",
|
|
25003
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
25004
|
+
duration_ms: 0,
|
|
25005
|
+
source: req.source,
|
|
25006
|
+
requestId: req.requestId
|
|
25007
|
+
},
|
|
25008
|
+
success: false,
|
|
25009
|
+
error: {
|
|
25010
|
+
code: "E_INVALID_OPERATION",
|
|
25011
|
+
exitCode: 2,
|
|
25012
|
+
message: `Operation not available at '${tier}' tier. Domain '${req.domain}' requires a higher tier.`
|
|
25013
|
+
}
|
|
25014
|
+
};
|
|
25015
|
+
}
|
|
25016
|
+
const response = await next();
|
|
25017
|
+
if (response.success && response.data !== void 0) {
|
|
25018
|
+
response.data = applyProjection(response.data, config);
|
|
25019
|
+
}
|
|
25020
|
+
return response;
|
|
25021
|
+
};
|
|
25022
|
+
}
|
|
25023
|
+
|
|
25024
|
+
// src/dispatch/adapters/mcp.ts
|
|
24175
25025
|
init_paths();
|
|
24176
25026
|
var _dispatcher = null;
|
|
24177
25027
|
function initMcpDispatcher(config = {}) {
|
|
@@ -24184,6 +25034,8 @@ function initMcpDispatcher(config = {}) {
|
|
|
24184
25034
|
createSessionResolver(),
|
|
24185
25035
|
// T4959: session identity first
|
|
24186
25036
|
createSanitizer(() => getProjectRoot()),
|
|
25037
|
+
createProjectionMiddleware(),
|
|
25038
|
+
// T5096: MVI tier-based domain/field projection
|
|
24187
25039
|
createFieldFilter(),
|
|
24188
25040
|
createRateLimiter(config.rateLimiting),
|
|
24189
25041
|
createVerificationGates(strictMode),
|