@kitsy/coop-core 1.0.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -72,6 +72,11 @@ __export(index_exports, {
72
72
  compute_readiness_with_corrections: () => compute_readiness_with_corrections,
73
73
  compute_score: () => compute_score,
74
74
  compute_velocity: () => compute_velocity,
75
+ coop_project_config_path: () => coop_project_config_path,
76
+ coop_project_root: () => coop_project_root,
77
+ coop_projects_dir: () => coop_projects_dir,
78
+ coop_workspace_config_path: () => coop_workspace_config_path,
79
+ coop_workspace_dir: () => coop_workspace_dir,
75
80
  createItem: () => createItem,
76
81
  create_seeded_rng: () => create_seeded_rng,
77
82
  critical_path_weight: () => critical_path_weight,
@@ -83,6 +88,7 @@ __export(index_exports, {
83
88
  effective_weekly_hours: () => effective_weekly_hours,
84
89
  effort_or_default: () => effort_or_default,
85
90
  ensureCoopLayout: () => ensureCoopLayout,
91
+ ensure_workspace_layout: () => ensure_workspace_layout,
86
92
  executor_fit_weight: () => executor_fit_weight,
87
93
  external_dependencies_for_task: () => external_dependencies_for_task,
88
94
  extract_subgraph: () => extract_subgraph,
@@ -91,7 +97,11 @@ __export(index_exports, {
91
97
  getItemById: () => getItemById,
92
98
  get_remaining_tokens: () => get_remaining_tokens,
93
99
  get_user_role: () => get_user_role,
100
+ has_legacy_project_layout: () => has_legacy_project_layout,
101
+ has_v2_projects_layout: () => has_v2_projects_layout,
94
102
  is_external_dependency: () => is_external_dependency,
103
+ is_project_initialized: () => is_project_initialized,
104
+ list_projects: () => list_projects,
95
105
  loadState: () => loadState,
96
106
  load_auth_config: () => load_auth_config,
97
107
  load_completed_runs: () => load_completed_runs,
@@ -116,9 +126,14 @@ __export(index_exports, {
116
126
  pert_stddev: () => pert_stddev,
117
127
  priority_weight: () => priority_weight,
118
128
  queryItems: () => queryItems,
129
+ read_project_config: () => read_project_config,
119
130
  read_schema_version: () => read_schema_version,
131
+ read_workspace_config: () => read_workspace_config,
120
132
  renderAgentPrompt: () => renderAgentPrompt,
133
+ repo_default_project_id: () => repo_default_project_id,
134
+ repo_default_project_name: () => repo_default_project_name,
121
135
  resolve_external_dependencies: () => resolve_external_dependencies,
136
+ resolve_project: () => resolve_project,
122
137
  risk_penalty: () => risk_penalty,
123
138
  run_hook: () => run_hook,
124
139
  run_monte_carlo_chunk: () => run_monte_carlo_chunk,
@@ -147,7 +162,8 @@ __export(index_exports, {
147
162
  validate_transition: () => validate_transition,
148
163
  writeTask: () => writeTask,
149
164
  writeYamlFile: () => writeYamlFile,
150
- write_schema_version: () => write_schema_version
165
+ write_schema_version: () => write_schema_version,
166
+ write_workspace_config: () => write_workspace_config
151
167
  });
152
168
  module.exports = __toCommonJS(index_exports);
153
169
 
@@ -4858,9 +4874,131 @@ function validate(task, context = {}) {
4858
4874
  };
4859
4875
  }
4860
4876
 
4861
- // src/core.ts
4877
+ // src/workspace.ts
4862
4878
  var import_node_fs14 = __toESM(require("fs"), 1);
4863
4879
  var import_node_path9 = __toESM(require("path"), 1);
4880
+ var COOP_DIR_NAME = ".coop";
4881
+ function sanitizeProjectId(value, fallback) {
4882
+ const normalized = value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").replace(/-+/g, "-");
4883
+ return normalized || fallback;
4884
+ }
4885
+ function coop_workspace_dir(repoRoot) {
4886
+ return import_node_path9.default.join(import_node_path9.default.resolve(repoRoot), COOP_DIR_NAME);
4887
+ }
4888
+ function coop_projects_dir(repoRoot) {
4889
+ return import_node_path9.default.join(coop_workspace_dir(repoRoot), "projects");
4890
+ }
4891
+ function coop_workspace_config_path(repoRoot) {
4892
+ return import_node_path9.default.join(coop_workspace_dir(repoRoot), "config.yml");
4893
+ }
4894
+ function coop_project_root(repoRoot, projectId) {
4895
+ return import_node_path9.default.join(coop_projects_dir(repoRoot), projectId);
4896
+ }
4897
+ function coop_project_config_path(projectRoot) {
4898
+ return import_node_path9.default.join(projectRoot, "config.yml");
4899
+ }
4900
+ function repo_default_project_id(repoRoot) {
4901
+ return sanitizeProjectId(import_node_path9.default.basename(import_node_path9.default.resolve(repoRoot)), "workspace");
4902
+ }
4903
+ function repo_default_project_name(repoRoot) {
4904
+ const base = import_node_path9.default.basename(import_node_path9.default.resolve(repoRoot)).trim();
4905
+ return base || "COOP Workspace";
4906
+ }
4907
+ function has_v2_projects_layout(repoRoot) {
4908
+ return import_node_fs14.default.existsSync(coop_projects_dir(repoRoot));
4909
+ }
4910
+ function has_legacy_project_layout(repoRoot) {
4911
+ const workspaceDir = coop_workspace_dir(repoRoot);
4912
+ return import_node_fs14.default.existsSync(workspaceDir) && import_node_fs14.default.existsSync(import_node_path9.default.join(workspaceDir, "config.yml")) && !import_node_fs14.default.existsSync(coop_projects_dir(repoRoot));
4913
+ }
4914
+ function read_workspace_config(repoRoot) {
4915
+ const configPath = coop_workspace_config_path(repoRoot);
4916
+ if (!import_node_fs14.default.existsSync(configPath) || has_legacy_project_layout(repoRoot)) {
4917
+ return { version: 2 };
4918
+ }
4919
+ return parseYamlFile(configPath);
4920
+ }
4921
+ function write_workspace_config(repoRoot, config) {
4922
+ import_node_fs14.default.mkdirSync(coop_workspace_dir(repoRoot), { recursive: true });
4923
+ writeYamlFile(coop_workspace_config_path(repoRoot), {
4924
+ version: config.version ?? 2,
4925
+ ...config.current_project ? { current_project: config.current_project } : {}
4926
+ });
4927
+ }
4928
+ function read_project_config(projectRoot) {
4929
+ return parseYamlFile(coop_project_config_path(projectRoot));
4930
+ }
4931
+ function project_ref_from_config(repoRoot, projectRoot, layout) {
4932
+ const config = read_project_config(projectRoot);
4933
+ const repoName = repo_default_project_name(repoRoot);
4934
+ const fallbackId = repo_default_project_id(repoRoot);
4935
+ return {
4936
+ id: sanitizeProjectId(config.project?.id ?? fallbackId, fallbackId),
4937
+ name: config.project?.name?.trim() || repoName,
4938
+ aliases: Array.isArray(config.project?.aliases) ? config.project.aliases.filter((entry) => typeof entry === "string" && entry.trim().length > 0) : [],
4939
+ root: projectRoot,
4940
+ repo_root: import_node_path9.default.resolve(repoRoot),
4941
+ layout
4942
+ };
4943
+ }
4944
+ function list_projects(repoRoot) {
4945
+ if (has_v2_projects_layout(repoRoot)) {
4946
+ const projectsDir = coop_projects_dir(repoRoot);
4947
+ return import_node_fs14.default.readdirSync(projectsDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => import_node_path9.default.join(projectsDir, entry.name)).filter((projectRoot) => import_node_fs14.default.existsSync(coop_project_config_path(projectRoot))).map((projectRoot) => project_ref_from_config(repoRoot, projectRoot, "v2")).sort((a, b) => a.id.localeCompare(b.id));
4948
+ }
4949
+ if (has_legacy_project_layout(repoRoot)) {
4950
+ return [project_ref_from_config(repoRoot, coop_workspace_dir(repoRoot), "legacy")];
4951
+ }
4952
+ return [];
4953
+ }
4954
+ function resolve_project(repoRoot, options = {}) {
4955
+ const projects = list_projects(repoRoot);
4956
+ const requested = options.project?.trim().toLowerCase();
4957
+ if (requested) {
4958
+ const match = projects.find(
4959
+ (project) => project.id.toLowerCase() === requested || project.name.toLowerCase() === requested || project.aliases.some((alias) => alias.toLowerCase() === requested)
4960
+ );
4961
+ if (!match) {
4962
+ throw new Error(`Project '${options.project}' not found.`);
4963
+ }
4964
+ return match;
4965
+ }
4966
+ if (projects.length === 1) {
4967
+ return projects[0];
4968
+ }
4969
+ const workspaceConfig = read_workspace_config(repoRoot);
4970
+ if (workspaceConfig.current_project) {
4971
+ const match = projects.find((project) => project.id === workspaceConfig.current_project);
4972
+ if (match) return match;
4973
+ }
4974
+ if (!options.require && projects.length === 0) {
4975
+ return {
4976
+ id: repo_default_project_id(repoRoot),
4977
+ name: repo_default_project_name(repoRoot),
4978
+ aliases: [],
4979
+ root: coop_project_root(repoRoot, repo_default_project_id(repoRoot)),
4980
+ repo_root: import_node_path9.default.resolve(repoRoot),
4981
+ layout: "v2"
4982
+ };
4983
+ }
4984
+ if (projects.length === 0) {
4985
+ throw new Error("No COOP project found. Run 'coop init'.");
4986
+ }
4987
+ throw new Error("Multiple COOP projects found. Pass --project <id> or run 'coop project use <id>'.");
4988
+ }
4989
+ function ensure_workspace_layout(repoRoot) {
4990
+ const workspaceDir = coop_workspace_dir(repoRoot);
4991
+ import_node_fs14.default.mkdirSync(workspaceDir, { recursive: true });
4992
+ import_node_fs14.default.mkdirSync(coop_projects_dir(repoRoot), { recursive: true });
4993
+ return workspaceDir;
4994
+ }
4995
+ function is_project_initialized(projectRoot) {
4996
+ return import_node_fs14.default.existsSync(coop_project_config_path(projectRoot));
4997
+ }
4998
+
4999
+ // src/core.ts
5000
+ var import_node_fs15 = __toESM(require("fs"), 1);
5001
+ var import_node_path10 = __toESM(require("path"), 1);
4864
5002
  var import_gray_matter = __toESM(require("gray-matter"), 1);
4865
5003
 
4866
5004
  // src/types.ts
@@ -4886,17 +5024,17 @@ function toIdKey(value) {
4886
5024
  return value.trim().toUpperCase();
4887
5025
  }
4888
5026
  function repoRootByPackage(cwd) {
4889
- let current = import_node_path9.default.resolve(cwd);
5027
+ let current = import_node_path10.default.resolve(cwd);
4890
5028
  let lastWorkspaceRoot = null;
4891
5029
  while (true) {
4892
- const packageJson = import_node_path9.default.join(current, "package.json");
4893
- const workspaceYaml = import_node_path9.default.join(current, "pnpm-workspace.yaml");
4894
- if (import_node_fs14.default.existsSync(packageJson) && import_node_fs14.default.existsSync(workspaceYaml)) {
5030
+ const packageJson = import_node_path10.default.join(current, "package.json");
5031
+ const workspaceYaml = import_node_path10.default.join(current, "pnpm-workspace.yaml");
5032
+ if (import_node_fs15.default.existsSync(packageJson) && import_node_fs15.default.existsSync(workspaceYaml)) {
4895
5033
  lastWorkspaceRoot = current;
4896
- const hasCoop = import_node_fs14.default.existsSync(import_node_path9.default.join(current, COOP_DIR, "config.yml"));
5034
+ const hasCoop = import_node_fs15.default.existsSync(import_node_path10.default.join(current, COOP_DIR, "config.yml"));
4897
5035
  if (hasCoop) return current;
4898
5036
  }
4899
- const parent = import_node_path9.default.dirname(current);
5037
+ const parent = import_node_path10.default.dirname(current);
4900
5038
  if (parent === current) return lastWorkspaceRoot;
4901
5039
  current = parent;
4902
5040
  }
@@ -4905,24 +5043,24 @@ function findRepoRoot(cwd = process.cwd()) {
4905
5043
  return repoRootByPackage(cwd);
4906
5044
  }
4907
5045
  function configPathFor(rootDir, workspaceDir) {
4908
- return import_node_path9.default.join(rootDir, workspaceDir, "config.yml");
5046
+ return import_node_path10.default.join(rootDir, workspaceDir, "config.yml");
4909
5047
  }
4910
5048
  function backlogPathFor(rootDir, workspaceDir) {
4911
- return import_node_path9.default.join(rootDir, workspaceDir, "backlog");
5049
+ return import_node_path10.default.join(rootDir, workspaceDir, "backlog");
4912
5050
  }
4913
5051
  function releasesPathFor(rootDir, workspaceDir) {
4914
- return import_node_path9.default.join(rootDir, workspaceDir, "releases");
5052
+ return import_node_path10.default.join(rootDir, workspaceDir, "releases");
4915
5053
  }
4916
5054
  function detectWorkspaceDir(rootDir) {
4917
- if (import_node_fs14.default.existsSync(configPathFor(rootDir, COOP_DIR))) return COOP_DIR;
4918
- if (import_node_fs14.default.existsSync(import_node_path9.default.join(rootDir, COOP_DIR))) return COOP_DIR;
5055
+ if (import_node_fs15.default.existsSync(configPathFor(rootDir, COOP_DIR))) return COOP_DIR;
5056
+ if (import_node_fs15.default.existsSync(import_node_path10.default.join(rootDir, COOP_DIR))) return COOP_DIR;
4919
5057
  return null;
4920
5058
  }
4921
5059
  function preferredWorkspaceDir(rootDir) {
4922
5060
  return detectWorkspaceDir(rootDir) ?? COOP_DIR;
4923
5061
  }
4924
5062
  function missingConfigError(rootDir) {
4925
- const coopConfig = import_node_path9.default.relative(rootDir, configPathFor(rootDir, COOP_DIR));
5063
+ const coopConfig = import_node_path10.default.relative(rootDir, configPathFor(rootDir, COOP_DIR));
4926
5064
  return new Error(`COOP config missing at ${coopConfig}. Run: coop init`);
4927
5065
  }
4928
5066
  function parseConfig(raw) {
@@ -4968,10 +5106,10 @@ function configToString(config) {
4968
5106
  return lines.join("\n");
4969
5107
  }
4970
5108
  function toPortablePath(value) {
4971
- return value.split(import_node_path9.default.sep).join("/");
5109
+ return value.split(import_node_path10.default.sep).join("/");
4972
5110
  }
4973
5111
  function ensureReleasesDir(rootDir, workspaceDir) {
4974
- import_node_fs14.default.mkdirSync(releasesPathFor(rootDir, workspaceDir), { recursive: true });
5112
+ import_node_fs15.default.mkdirSync(releasesPathFor(rootDir, workspaceDir), { recursive: true });
4975
5113
  }
4976
5114
  function releaseHeader(date) {
4977
5115
  return `## ${date}`;
@@ -4993,12 +5131,12 @@ function appendReleaseEntry(rootDir, workspaceDir, item, previousStatus, nextSta
4993
5131
  ensureReleasesDir(rootDir, workspaceDir);
4994
5132
  const now = /* @__PURE__ */ new Date();
4995
5133
  const date = now.toISOString().slice(0, 10);
4996
- const releasePath = import_node_path9.default.join(releasesPathFor(rootDir, workspaceDir), `${date}.md`);
5134
+ const releasePath = import_node_path10.default.join(releasesPathFor(rootDir, workspaceDir), `${date}.md`);
4997
5135
  const heading = "# COOP Release Notes";
4998
5136
  const dayHeader = releaseHeader(date);
4999
5137
  const entry = releaseEntryLine(item, previousStatus, nextStatus);
5000
- if (!import_node_fs14.default.existsSync(releasePath)) {
5001
- import_node_fs14.default.writeFileSync(
5138
+ if (!import_node_fs15.default.existsSync(releasePath)) {
5139
+ import_node_fs15.default.writeFileSync(
5002
5140
  releasePath,
5003
5141
  [
5004
5142
  `${heading}
@@ -5011,10 +5149,10 @@ function appendReleaseEntry(rootDir, workspaceDir, item, previousStatus, nextSta
5011
5149
  ].join("\n"),
5012
5150
  "utf8"
5013
5151
  );
5014
- return toPortablePath(import_node_path9.default.relative(rootDir, releasePath));
5152
+ return toPortablePath(import_node_path10.default.relative(rootDir, releasePath));
5015
5153
  }
5016
- const existing = import_node_fs14.default.readFileSync(releasePath, "utf8");
5017
- if (hasReleaseEntry(existing, item.id)) return toPortablePath(import_node_path9.default.relative(rootDir, releasePath));
5154
+ const existing = import_node_fs15.default.readFileSync(releasePath, "utf8");
5155
+ if (hasReleaseEntry(existing, item.id)) return toPortablePath(import_node_path10.default.relative(rootDir, releasePath));
5018
5156
  let nextContent = existing;
5019
5157
  if (!existing.includes(`## ${date}`)) {
5020
5158
  if (!nextContent.endsWith("\n")) nextContent += "\n";
@@ -5024,9 +5162,9 @@ function appendReleaseEntry(rootDir, workspaceDir, item, previousStatus, nextSta
5024
5162
  if (!nextContent.endsWith("\n")) nextContent += "\n";
5025
5163
  nextContent += `${entry}
5026
5164
  `;
5027
- import_node_fs14.default.writeFileSync(releasePath, `${nextContent}
5165
+ import_node_fs15.default.writeFileSync(releasePath, `${nextContent}
5028
5166
  `, "utf8");
5029
- return toPortablePath(import_node_path9.default.relative(rootDir, releasePath));
5167
+ return toPortablePath(import_node_path10.default.relative(rootDir, releasePath));
5030
5168
  }
5031
5169
  function completeItem(rootDir, id) {
5032
5170
  const state = loadState(rootDir);
@@ -5104,28 +5242,28 @@ function validateAndNormalize(data, sourceFile) {
5104
5242
  };
5105
5243
  }
5106
5244
  function parseItem(filePath, rootDir) {
5107
- const raw = import_node_fs14.default.readFileSync(filePath, "utf8");
5245
+ const raw = import_node_fs15.default.readFileSync(filePath, "utf8");
5108
5246
  const parsed = (0, import_gray_matter.default)(raw);
5109
- const data = validateAndNormalize(parsed.data, import_node_path9.default.relative(rootDir, filePath));
5247
+ const data = validateAndNormalize(parsed.data, import_node_path10.default.relative(rootDir, filePath));
5110
5248
  return {
5111
5249
  ...data,
5112
5250
  body: parsed.content || "",
5113
- filePath: import_node_path9.default.relative(rootDir, filePath)
5251
+ filePath: import_node_path10.default.relative(rootDir, filePath)
5114
5252
  };
5115
5253
  }
5116
5254
  function walk(dir) {
5117
5255
  const out = [];
5118
- if (!import_node_fs14.default.existsSync(dir)) return out;
5119
- const entries = import_node_fs14.default.readdirSync(dir, { withFileTypes: true });
5256
+ if (!import_node_fs15.default.existsSync(dir)) return out;
5257
+ const entries = import_node_fs15.default.readdirSync(dir, { withFileTypes: true });
5120
5258
  for (const entry of entries) {
5121
- const file = import_node_path9.default.join(dir, entry.name);
5259
+ const file = import_node_path10.default.join(dir, entry.name);
5122
5260
  if (entry.isDirectory()) out.push(...walk(file));
5123
5261
  if (entry.isFile() && file.endsWith(".md")) out.push(file);
5124
5262
  }
5125
5263
  return out;
5126
5264
  }
5127
5265
  function itemPath(type, id, rootDir, workspaceDir) {
5128
- return import_node_path9.default.join(backlogPathFor(rootDir, workspaceDir), ITEM_DIRS[type], `${id}.md`);
5266
+ return import_node_path10.default.join(backlogPathFor(rootDir, workspaceDir), ITEM_DIRS[type], `${id}.md`);
5129
5267
  }
5130
5268
  function normalizeFrontmatterValue(value) {
5131
5269
  if (value == null) return void 0;
@@ -5212,30 +5350,30 @@ function nextGeneratedId(config, title, existing) {
5212
5350
  }
5213
5351
  function ensureCoopLayout(rootDir) {
5214
5352
  const workspaceDir = preferredWorkspaceDir(rootDir);
5215
- const root = import_node_path9.default.join(rootDir, workspaceDir);
5216
- import_node_fs14.default.mkdirSync(root, { recursive: true });
5217
- import_node_fs14.default.mkdirSync(import_node_path9.default.join(root, "releases"), { recursive: true });
5218
- import_node_fs14.default.mkdirSync(import_node_path9.default.join(root, "plans"), { recursive: true });
5219
- import_node_fs14.default.mkdirSync(import_node_path9.default.join(root, "views"), { recursive: true });
5220
- import_node_fs14.default.mkdirSync(import_node_path9.default.join(root, "templates"), { recursive: true });
5353
+ const root = import_node_path10.default.join(rootDir, workspaceDir);
5354
+ import_node_fs15.default.mkdirSync(root, { recursive: true });
5355
+ import_node_fs15.default.mkdirSync(import_node_path10.default.join(root, "releases"), { recursive: true });
5356
+ import_node_fs15.default.mkdirSync(import_node_path10.default.join(root, "plans"), { recursive: true });
5357
+ import_node_fs15.default.mkdirSync(import_node_path10.default.join(root, "views"), { recursive: true });
5358
+ import_node_fs15.default.mkdirSync(import_node_path10.default.join(root, "templates"), { recursive: true });
5221
5359
  for (const dir of Object.values(ITEM_DIRS)) {
5222
- import_node_fs14.default.mkdirSync(import_node_path9.default.join(root, "backlog", dir), { recursive: true });
5360
+ import_node_fs15.default.mkdirSync(import_node_path10.default.join(root, "backlog", dir), { recursive: true });
5223
5361
  }
5224
- const configFile = import_node_path9.default.join(root, "config.yml");
5225
- if (!import_node_fs14.default.existsSync(configFile)) {
5226
- import_node_fs14.default.writeFileSync(configFile, configToString(DEFAULT_CONFIG3), "utf8");
5362
+ const configFile = import_node_path10.default.join(root, "config.yml");
5363
+ if (!import_node_fs15.default.existsSync(configFile)) {
5364
+ import_node_fs15.default.writeFileSync(configFile, configToString(DEFAULT_CONFIG3), "utf8");
5227
5365
  }
5228
5366
  }
5229
5367
  function loadState(rootDir) {
5230
5368
  const workspaceDir = detectWorkspaceDir(rootDir);
5231
5369
  if (!workspaceDir) throw missingConfigError(rootDir);
5232
5370
  const configPath = configPathFor(rootDir, workspaceDir);
5233
- if (!import_node_fs14.default.existsSync(configPath)) throw missingConfigError(rootDir);
5234
- const config = parseConfig(import_node_fs14.default.readFileSync(configPath, "utf8"));
5371
+ if (!import_node_fs15.default.existsSync(configPath)) throw missingConfigError(rootDir);
5372
+ const config = parseConfig(import_node_fs15.default.readFileSync(configPath, "utf8"));
5235
5373
  const items = [];
5236
5374
  const itemsById = /* @__PURE__ */ new Map();
5237
5375
  for (const type of ITEM_TYPES) {
5238
- const dir = import_node_path9.default.join(backlogPathFor(rootDir, workspaceDir), ITEM_DIRS[type]);
5376
+ const dir = import_node_path10.default.join(backlogPathFor(rootDir, workspaceDir), ITEM_DIRS[type]);
5239
5377
  const files = walk(dir);
5240
5378
  for (const file of files) {
5241
5379
  const item = parseItem(file, rootDir);
@@ -5319,21 +5457,21 @@ function createItem(rootDir, params) {
5319
5457
  parent_id: params.parent_id
5320
5458
  };
5321
5459
  const itemPathName = itemPath(params.type, item.id, rootDir, state.workspaceDir);
5322
- import_node_fs14.default.writeFileSync(itemPathName, serialize(item, params.body || ""), "utf8");
5460
+ import_node_fs15.default.writeFileSync(itemPathName, serialize(item, params.body || ""), "utf8");
5323
5461
  if ((config.id_strategy ?? "text") === "counter") {
5324
5462
  const numericMatch = /-(\d+)$/.exec(item.id);
5325
5463
  if (numericMatch) {
5326
5464
  const numericValue = Number(numericMatch[1]);
5327
5465
  if (Number.isInteger(numericValue) && numericValue >= (config.next_id ?? 1)) {
5328
5466
  config.next_id = numericValue + 1;
5329
- import_node_fs14.default.writeFileSync(configPathFor(rootDir, state.workspaceDir), configToString(config), "utf8");
5467
+ import_node_fs15.default.writeFileSync(configPathFor(rootDir, state.workspaceDir), configToString(config), "utf8");
5330
5468
  }
5331
5469
  }
5332
5470
  }
5333
5471
  return {
5334
5472
  ...item,
5335
5473
  body: params.body || "",
5336
- filePath: import_node_path9.default.relative(rootDir, itemPathName)
5474
+ filePath: import_node_path10.default.relative(rootDir, itemPathName)
5337
5475
  };
5338
5476
  }
5339
5477
  function updateItem(rootDir, id, patch) {
@@ -5359,8 +5497,8 @@ function updateItem(rootDir, id, patch) {
5359
5497
  };
5360
5498
  if (!ITEM_TYPES.includes(next.type)) throw new Error(`Unknown type ${next.type}.`);
5361
5499
  if (!ITEM_STATUSES.includes(next.status)) throw new Error(`Unknown status ${next.status}.`);
5362
- const filePath = import_node_path9.default.join(rootDir, existing.filePath);
5363
- import_node_fs14.default.writeFileSync(filePath, serialize(next, patch.body || existing.body), "utf8");
5500
+ const filePath = import_node_path10.default.join(rootDir, existing.filePath);
5501
+ import_node_fs15.default.writeFileSync(filePath, serialize(next, patch.body || existing.body), "utf8");
5364
5502
  return {
5365
5503
  ...next,
5366
5504
  body: patch.body || existing.body,
@@ -5376,7 +5514,7 @@ function deleteItem(rootDir, id) {
5376
5514
  if (children.length > 0) {
5377
5515
  throw new Error(`Cannot delete ${existing.id} because it has ${children.length} child item(s). Remove children first.`);
5378
5516
  }
5379
- import_node_fs14.default.unlinkSync(import_node_path9.default.join(rootDir, existing.filePath));
5517
+ import_node_fs15.default.unlinkSync(import_node_path10.default.join(rootDir, existing.filePath));
5380
5518
  return existing;
5381
5519
  }
5382
5520
  function renderAgentPrompt(item) {
@@ -5415,8 +5553,8 @@ function validateRepo(rootDir) {
5415
5553
  const errors = [];
5416
5554
  const warnings = [];
5417
5555
  const workspaceDir = detectWorkspaceDir(rootDir);
5418
- if (!workspaceDir || !import_node_fs14.default.existsSync(configPathFor(rootDir, workspaceDir))) {
5419
- errors.push("Missing .coop/config.yml. Run coop init first.");
5556
+ if (!workspaceDir || !import_node_fs15.default.existsSync(configPathFor(rootDir, workspaceDir))) {
5557
+ errors.push("Missing COOP config. Run coop init first.");
5420
5558
  return { valid: false, errors, warnings };
5421
5559
  }
5422
5560
  let state;
@@ -5490,6 +5628,11 @@ function validateRepo(rootDir) {
5490
5628
  compute_readiness_with_corrections,
5491
5629
  compute_score,
5492
5630
  compute_velocity,
5631
+ coop_project_config_path,
5632
+ coop_project_root,
5633
+ coop_projects_dir,
5634
+ coop_workspace_config_path,
5635
+ coop_workspace_dir,
5493
5636
  createItem,
5494
5637
  create_seeded_rng,
5495
5638
  critical_path_weight,
@@ -5501,6 +5644,7 @@ function validateRepo(rootDir) {
5501
5644
  effective_weekly_hours,
5502
5645
  effort_or_default,
5503
5646
  ensureCoopLayout,
5647
+ ensure_workspace_layout,
5504
5648
  executor_fit_weight,
5505
5649
  external_dependencies_for_task,
5506
5650
  extract_subgraph,
@@ -5509,7 +5653,11 @@ function validateRepo(rootDir) {
5509
5653
  getItemById,
5510
5654
  get_remaining_tokens,
5511
5655
  get_user_role,
5656
+ has_legacy_project_layout,
5657
+ has_v2_projects_layout,
5512
5658
  is_external_dependency,
5659
+ is_project_initialized,
5660
+ list_projects,
5513
5661
  loadState,
5514
5662
  load_auth_config,
5515
5663
  load_completed_runs,
@@ -5534,9 +5682,14 @@ function validateRepo(rootDir) {
5534
5682
  pert_stddev,
5535
5683
  priority_weight,
5536
5684
  queryItems,
5685
+ read_project_config,
5537
5686
  read_schema_version,
5687
+ read_workspace_config,
5538
5688
  renderAgentPrompt,
5689
+ repo_default_project_id,
5690
+ repo_default_project_name,
5539
5691
  resolve_external_dependencies,
5692
+ resolve_project,
5540
5693
  risk_penalty,
5541
5694
  run_hook,
5542
5695
  run_monte_carlo_chunk,
@@ -5565,5 +5718,6 @@ function validateRepo(rootDir) {
5565
5718
  validate_transition,
5566
5719
  writeTask,
5567
5720
  writeYamlFile,
5568
- write_schema_version
5721
+ write_schema_version,
5722
+ write_workspace_config
5569
5723
  });
package/dist/index.d.cts CHANGED
@@ -1386,6 +1386,39 @@ interface ValidationResult {
1386
1386
  }
1387
1387
  declare function validate(task: Task, context?: ValidationContext): ValidationResult;
1388
1388
 
1389
+ type CoopWorkspaceConfig = {
1390
+ version?: number;
1391
+ current_project?: string;
1392
+ };
1393
+ type CoopProjectRef = {
1394
+ id: string;
1395
+ name: string;
1396
+ aliases: string[];
1397
+ root: string;
1398
+ repo_root: string;
1399
+ layout: "legacy" | "v2";
1400
+ };
1401
+ type ResolveProjectOptions = {
1402
+ project?: string;
1403
+ require?: boolean;
1404
+ };
1405
+ declare function coop_workspace_dir(repoRoot: string): string;
1406
+ declare function coop_projects_dir(repoRoot: string): string;
1407
+ declare function coop_workspace_config_path(repoRoot: string): string;
1408
+ declare function coop_project_root(repoRoot: string, projectId: string): string;
1409
+ declare function coop_project_config_path(projectRoot: string): string;
1410
+ declare function repo_default_project_id(repoRoot: string): string;
1411
+ declare function repo_default_project_name(repoRoot: string): string;
1412
+ declare function has_v2_projects_layout(repoRoot: string): boolean;
1413
+ declare function has_legacy_project_layout(repoRoot: string): boolean;
1414
+ declare function read_workspace_config(repoRoot: string): CoopWorkspaceConfig;
1415
+ declare function write_workspace_config(repoRoot: string, config: CoopWorkspaceConfig): void;
1416
+ declare function read_project_config(projectRoot: string): CoopConfig;
1417
+ declare function list_projects(repoRoot: string): CoopProjectRef[];
1418
+ declare function resolve_project(repoRoot: string, options?: ResolveProjectOptions): CoopProjectRef;
1419
+ declare function ensure_workspace_layout(repoRoot: string): string;
1420
+ declare function is_project_initialized(projectRoot: string): boolean;
1421
+
1389
1422
  /**
1390
1423
  * Finds the nearest workspace root that contains package + workspace metadata.
1391
1424
  * [SPEC: Architecture v2.0 §2]
@@ -1452,4 +1485,4 @@ declare function validateRepo(rootDir: string): {
1452
1485
  warnings: string[];
1453
1486
  };
1454
1487
 
1455
- export { type AIAgent, type AIResource, type AgentSpec, type AllocationResult, ArtifactType, type AuthConfig, type AuthPolicy, type AutoTransitionResult, type AvailabilityWindow, type BacklogItem, type BacklogItemData, COOP_EVENT_TYPES, CURRENT_SCHEMA_VERSION, type CapacityLedger, type ComparisonResult, type ComparisonRow, type ComputeNode, type ComputeResource, type CoopConfig, type CoopEvent, CoopEventEmitter, type CoopEventType, type CreateItemParams, type CriticalPathResult, type CriticalPathTaskMetrics, DEFAULT_SCORE_WEIGHTS, type Delivery, type DeliveryAtRisk, type DeliveryBudget, type DeliveryCommitted, type DeliveryGovernance, type DeliveryRisk, type DeliveryRiskType, type DeliveryScope, DeliveryStatus, type DetectedDeliveryRisk, type EffectiveCapacity, type EventByType, type ExecutionConstraints, type ExecutionPermissions, ExecutorType, type ExternalDependencyRef, type ExternalDependencyResolution, type ExternalDependencyResolutionStatus, type ExternalDependencyResolverOptions, type ExternalRepoConfig, type FeasibilityResult, type FeasibilityRisk, type FeasibilityStatus, type FeasibilitySummary, type FilterSpec, type FrontmatterParseResult, type GraphCycleDetected, type GraphValidationContext, type GraphValidationResult, type HookRunResult, type HumanMember, type HumanResource, ITEM_STATUSES, ITEM_TYPES, type Idea, IdeaStatus, IndexManager, type IndexStatus, type ItemLinks, type ItemStatus, type ItemType, MIGRATIONS, type MigrateRepositoryOptions, type MigrationContext, type MigrationDefinition, type MigrationReport, type MonteCarloHistogramBucket, type MonteCarloOptions, type MonteCarloResult, type MonteCarloWorkerPayload, type ParsedDelivery, type ParsedIdea, type ParsedTask, type Permission, type PermissionContext, type PluginAction, type PluginActionConsole, type PluginActionGitHubPr, type PluginActionHandler, type PluginActionHandlerResult, type PluginActionWebhook, type PluginManifest, type PluginRunOptions, type PluginRunRecord, type PluginTrigger, type PolicyAction, type ReadinessComputation, type ReadinessPartitions, type ReadinessState, type ReadinessTransitionEvent, type ReadinessWarning, type ReferentialValidationContext, type RepoConfig, type RepoState, type ResourceProfile, RiskLevel, type Role, type Run, type RunCompleted, type RunFailed, type RunResourcesConsumed, type RunStarted, RunStatus, type RunStepResult, RunStepStatus, RunbookAction, type RunbookStep, type ScheduleOptions, type ScoreContext, type ScoredTask, type SemanticValidationContext, type SimulationResult, type StructuralValidationContext, type Task, type TaskAssigned, TaskComplexity, type TaskComputed, type TaskCore, type TaskCreated, TaskDeterminism, type TaskEstimate, type TaskEstimation, type TaskExecution, type TaskGovernance, type TaskGraph, type TaskPlanning, TaskPriority, type TaskResources, type TaskScheduleEntry, TaskStatus, type TaskTransitioned, type TaskTransitionedEvent, TaskType, type Track, type TrackUtilization, type TrackWip, type TransitionContext, type TransitionResult, type TransitionValidationContext, type UpdateItemParams, VALID_TASK_TRANSITIONS, VALID_TRANSITIONS, type ValidationContext, type ValidationError, type ValidationLevel, type ValidationResult, type VelocityMetrics, type VelocityPoint, type VelocityTrend, type WhatIfBaseline, type WhatIfModification, type WriteTaskOptions, allocate, allocate_ai, allocate_ai_tokens, analyze_feasibility, analyze_what_if, build_capacity_ledger, build_graph, check_blocked, check_permission, check_unblocked, check_wip, completeItem, complexity_penalty, compute_all_readiness, compute_critical_path, compute_readiness, compute_readiness_with_corrections, compute_score, compute_velocity, createItem, create_seeded_rng, critical_path_weight, deleteItem, dependency_unlock_weight, detect_cycle, detect_delivery_risks, determinism_weight, effective_weekly_hours, effort_or_default, ensureCoopLayout, executor_fit_weight, external_dependencies_for_task, extract_subgraph, findRepoRoot, find_external_dependencies, getItemById, get_remaining_tokens, get_user_role, is_external_dependency, loadState, load_auth_config, load_completed_runs, load_graph, load_plugins, migrate_repository, migrate_task, monte_carlo_forecast, parseDeliveryContent, parseDeliveryFile, parseFrontmatterContent, parseFrontmatterFile, parseIdeaContent, parseIdeaFile, parseTaskContent, parseTaskFile, parseYamlContent, parseYamlFile, parse_external_dependency, partition_by_readiness, pert_hours, pert_stddev, priority_weight, queryItems, read_schema_version, renderAgentPrompt, resolve_external_dependencies, risk_penalty, run_hook, run_monte_carlo_chunk, run_plugins_for_event, sample_pert_beta, sample_task_hours, schedule_next, simulate_schedule, stringifyFrontmatter, stringifyYamlContent, task_effort_hours, topological_sort, transition, transitive_dependencies, transitive_dependents, type_weight, updateItem, urgency_weight, validate, validateReferential, validateRepo, validateSemantic, validateStructural, validateTransition, validate_graph, validate_transition, writeTask, writeYamlFile, write_schema_version };
1488
+ export { type AIAgent, type AIResource, type AgentSpec, type AllocationResult, ArtifactType, type AuthConfig, type AuthPolicy, type AutoTransitionResult, type AvailabilityWindow, type BacklogItem, type BacklogItemData, COOP_EVENT_TYPES, CURRENT_SCHEMA_VERSION, type CapacityLedger, type ComparisonResult, type ComparisonRow, type ComputeNode, type ComputeResource, type CoopConfig, type CoopEvent, CoopEventEmitter, type CoopEventType, type CoopProjectRef, type CoopWorkspaceConfig, type CreateItemParams, type CriticalPathResult, type CriticalPathTaskMetrics, DEFAULT_SCORE_WEIGHTS, type Delivery, type DeliveryAtRisk, type DeliveryBudget, type DeliveryCommitted, type DeliveryGovernance, type DeliveryRisk, type DeliveryRiskType, type DeliveryScope, DeliveryStatus, type DetectedDeliveryRisk, type EffectiveCapacity, type EventByType, type ExecutionConstraints, type ExecutionPermissions, ExecutorType, type ExternalDependencyRef, type ExternalDependencyResolution, type ExternalDependencyResolutionStatus, type ExternalDependencyResolverOptions, type ExternalRepoConfig, type FeasibilityResult, type FeasibilityRisk, type FeasibilityStatus, type FeasibilitySummary, type FilterSpec, type FrontmatterParseResult, type GraphCycleDetected, type GraphValidationContext, type GraphValidationResult, type HookRunResult, type HumanMember, type HumanResource, ITEM_STATUSES, ITEM_TYPES, type Idea, IdeaStatus, IndexManager, type IndexStatus, type ItemLinks, type ItemStatus, type ItemType, MIGRATIONS, type MigrateRepositoryOptions, type MigrationContext, type MigrationDefinition, type MigrationReport, type MonteCarloHistogramBucket, type MonteCarloOptions, type MonteCarloResult, type MonteCarloWorkerPayload, type ParsedDelivery, type ParsedIdea, type ParsedTask, type Permission, type PermissionContext, type PluginAction, type PluginActionConsole, type PluginActionGitHubPr, type PluginActionHandler, type PluginActionHandlerResult, type PluginActionWebhook, type PluginManifest, type PluginRunOptions, type PluginRunRecord, type PluginTrigger, type PolicyAction, type ReadinessComputation, type ReadinessPartitions, type ReadinessState, type ReadinessTransitionEvent, type ReadinessWarning, type ReferentialValidationContext, type RepoConfig, type RepoState, type ResourceProfile, RiskLevel, type Role, type Run, type RunCompleted, type RunFailed, type RunResourcesConsumed, type RunStarted, RunStatus, type RunStepResult, RunStepStatus, RunbookAction, type RunbookStep, type ScheduleOptions, type ScoreContext, type ScoredTask, type SemanticValidationContext, type SimulationResult, type StructuralValidationContext, type Task, type TaskAssigned, TaskComplexity, type TaskComputed, type TaskCore, type TaskCreated, TaskDeterminism, type TaskEstimate, type TaskEstimation, type TaskExecution, type TaskGovernance, type TaskGraph, type TaskPlanning, TaskPriority, type TaskResources, type TaskScheduleEntry, TaskStatus, type TaskTransitioned, type TaskTransitionedEvent, TaskType, type Track, type TrackUtilization, type TrackWip, type TransitionContext, type TransitionResult, type TransitionValidationContext, type UpdateItemParams, VALID_TASK_TRANSITIONS, VALID_TRANSITIONS, type ValidationContext, type ValidationError, type ValidationLevel, type ValidationResult, type VelocityMetrics, type VelocityPoint, type VelocityTrend, type WhatIfBaseline, type WhatIfModification, type WriteTaskOptions, allocate, allocate_ai, allocate_ai_tokens, analyze_feasibility, analyze_what_if, build_capacity_ledger, build_graph, check_blocked, check_permission, check_unblocked, check_wip, completeItem, complexity_penalty, compute_all_readiness, compute_critical_path, compute_readiness, compute_readiness_with_corrections, compute_score, compute_velocity, coop_project_config_path, coop_project_root, coop_projects_dir, coop_workspace_config_path, coop_workspace_dir, createItem, create_seeded_rng, critical_path_weight, deleteItem, dependency_unlock_weight, detect_cycle, detect_delivery_risks, determinism_weight, effective_weekly_hours, effort_or_default, ensureCoopLayout, ensure_workspace_layout, executor_fit_weight, external_dependencies_for_task, extract_subgraph, findRepoRoot, find_external_dependencies, getItemById, get_remaining_tokens, get_user_role, has_legacy_project_layout, has_v2_projects_layout, is_external_dependency, is_project_initialized, list_projects, loadState, load_auth_config, load_completed_runs, load_graph, load_plugins, migrate_repository, migrate_task, monte_carlo_forecast, parseDeliveryContent, parseDeliveryFile, parseFrontmatterContent, parseFrontmatterFile, parseIdeaContent, parseIdeaFile, parseTaskContent, parseTaskFile, parseYamlContent, parseYamlFile, parse_external_dependency, partition_by_readiness, pert_hours, pert_stddev, priority_weight, queryItems, read_project_config, read_schema_version, read_workspace_config, renderAgentPrompt, repo_default_project_id, repo_default_project_name, resolve_external_dependencies, resolve_project, risk_penalty, run_hook, run_monte_carlo_chunk, run_plugins_for_event, sample_pert_beta, sample_task_hours, schedule_next, simulate_schedule, stringifyFrontmatter, stringifyYamlContent, task_effort_hours, topological_sort, transition, transitive_dependencies, transitive_dependents, type_weight, updateItem, urgency_weight, validate, validateReferential, validateRepo, validateSemantic, validateStructural, validateTransition, validate_graph, validate_transition, writeTask, writeYamlFile, write_schema_version, write_workspace_config };
package/dist/index.d.ts CHANGED
@@ -1386,6 +1386,39 @@ interface ValidationResult {
1386
1386
  }
1387
1387
  declare function validate(task: Task, context?: ValidationContext): ValidationResult;
1388
1388
 
1389
+ type CoopWorkspaceConfig = {
1390
+ version?: number;
1391
+ current_project?: string;
1392
+ };
1393
+ type CoopProjectRef = {
1394
+ id: string;
1395
+ name: string;
1396
+ aliases: string[];
1397
+ root: string;
1398
+ repo_root: string;
1399
+ layout: "legacy" | "v2";
1400
+ };
1401
+ type ResolveProjectOptions = {
1402
+ project?: string;
1403
+ require?: boolean;
1404
+ };
1405
+ declare function coop_workspace_dir(repoRoot: string): string;
1406
+ declare function coop_projects_dir(repoRoot: string): string;
1407
+ declare function coop_workspace_config_path(repoRoot: string): string;
1408
+ declare function coop_project_root(repoRoot: string, projectId: string): string;
1409
+ declare function coop_project_config_path(projectRoot: string): string;
1410
+ declare function repo_default_project_id(repoRoot: string): string;
1411
+ declare function repo_default_project_name(repoRoot: string): string;
1412
+ declare function has_v2_projects_layout(repoRoot: string): boolean;
1413
+ declare function has_legacy_project_layout(repoRoot: string): boolean;
1414
+ declare function read_workspace_config(repoRoot: string): CoopWorkspaceConfig;
1415
+ declare function write_workspace_config(repoRoot: string, config: CoopWorkspaceConfig): void;
1416
+ declare function read_project_config(projectRoot: string): CoopConfig;
1417
+ declare function list_projects(repoRoot: string): CoopProjectRef[];
1418
+ declare function resolve_project(repoRoot: string, options?: ResolveProjectOptions): CoopProjectRef;
1419
+ declare function ensure_workspace_layout(repoRoot: string): string;
1420
+ declare function is_project_initialized(projectRoot: string): boolean;
1421
+
1389
1422
  /**
1390
1423
  * Finds the nearest workspace root that contains package + workspace metadata.
1391
1424
  * [SPEC: Architecture v2.0 §2]
@@ -1452,4 +1485,4 @@ declare function validateRepo(rootDir: string): {
1452
1485
  warnings: string[];
1453
1486
  };
1454
1487
 
1455
- export { type AIAgent, type AIResource, type AgentSpec, type AllocationResult, ArtifactType, type AuthConfig, type AuthPolicy, type AutoTransitionResult, type AvailabilityWindow, type BacklogItem, type BacklogItemData, COOP_EVENT_TYPES, CURRENT_SCHEMA_VERSION, type CapacityLedger, type ComparisonResult, type ComparisonRow, type ComputeNode, type ComputeResource, type CoopConfig, type CoopEvent, CoopEventEmitter, type CoopEventType, type CreateItemParams, type CriticalPathResult, type CriticalPathTaskMetrics, DEFAULT_SCORE_WEIGHTS, type Delivery, type DeliveryAtRisk, type DeliveryBudget, type DeliveryCommitted, type DeliveryGovernance, type DeliveryRisk, type DeliveryRiskType, type DeliveryScope, DeliveryStatus, type DetectedDeliveryRisk, type EffectiveCapacity, type EventByType, type ExecutionConstraints, type ExecutionPermissions, ExecutorType, type ExternalDependencyRef, type ExternalDependencyResolution, type ExternalDependencyResolutionStatus, type ExternalDependencyResolverOptions, type ExternalRepoConfig, type FeasibilityResult, type FeasibilityRisk, type FeasibilityStatus, type FeasibilitySummary, type FilterSpec, type FrontmatterParseResult, type GraphCycleDetected, type GraphValidationContext, type GraphValidationResult, type HookRunResult, type HumanMember, type HumanResource, ITEM_STATUSES, ITEM_TYPES, type Idea, IdeaStatus, IndexManager, type IndexStatus, type ItemLinks, type ItemStatus, type ItemType, MIGRATIONS, type MigrateRepositoryOptions, type MigrationContext, type MigrationDefinition, type MigrationReport, type MonteCarloHistogramBucket, type MonteCarloOptions, type MonteCarloResult, type MonteCarloWorkerPayload, type ParsedDelivery, type ParsedIdea, type ParsedTask, type Permission, type PermissionContext, type PluginAction, type PluginActionConsole, type PluginActionGitHubPr, type PluginActionHandler, type PluginActionHandlerResult, type PluginActionWebhook, type PluginManifest, type PluginRunOptions, type PluginRunRecord, type PluginTrigger, type PolicyAction, type ReadinessComputation, type ReadinessPartitions, type ReadinessState, type ReadinessTransitionEvent, type ReadinessWarning, type ReferentialValidationContext, type RepoConfig, type RepoState, type ResourceProfile, RiskLevel, type Role, type Run, type RunCompleted, type RunFailed, type RunResourcesConsumed, type RunStarted, RunStatus, type RunStepResult, RunStepStatus, RunbookAction, type RunbookStep, type ScheduleOptions, type ScoreContext, type ScoredTask, type SemanticValidationContext, type SimulationResult, type StructuralValidationContext, type Task, type TaskAssigned, TaskComplexity, type TaskComputed, type TaskCore, type TaskCreated, TaskDeterminism, type TaskEstimate, type TaskEstimation, type TaskExecution, type TaskGovernance, type TaskGraph, type TaskPlanning, TaskPriority, type TaskResources, type TaskScheduleEntry, TaskStatus, type TaskTransitioned, type TaskTransitionedEvent, TaskType, type Track, type TrackUtilization, type TrackWip, type TransitionContext, type TransitionResult, type TransitionValidationContext, type UpdateItemParams, VALID_TASK_TRANSITIONS, VALID_TRANSITIONS, type ValidationContext, type ValidationError, type ValidationLevel, type ValidationResult, type VelocityMetrics, type VelocityPoint, type VelocityTrend, type WhatIfBaseline, type WhatIfModification, type WriteTaskOptions, allocate, allocate_ai, allocate_ai_tokens, analyze_feasibility, analyze_what_if, build_capacity_ledger, build_graph, check_blocked, check_permission, check_unblocked, check_wip, completeItem, complexity_penalty, compute_all_readiness, compute_critical_path, compute_readiness, compute_readiness_with_corrections, compute_score, compute_velocity, createItem, create_seeded_rng, critical_path_weight, deleteItem, dependency_unlock_weight, detect_cycle, detect_delivery_risks, determinism_weight, effective_weekly_hours, effort_or_default, ensureCoopLayout, executor_fit_weight, external_dependencies_for_task, extract_subgraph, findRepoRoot, find_external_dependencies, getItemById, get_remaining_tokens, get_user_role, is_external_dependency, loadState, load_auth_config, load_completed_runs, load_graph, load_plugins, migrate_repository, migrate_task, monte_carlo_forecast, parseDeliveryContent, parseDeliveryFile, parseFrontmatterContent, parseFrontmatterFile, parseIdeaContent, parseIdeaFile, parseTaskContent, parseTaskFile, parseYamlContent, parseYamlFile, parse_external_dependency, partition_by_readiness, pert_hours, pert_stddev, priority_weight, queryItems, read_schema_version, renderAgentPrompt, resolve_external_dependencies, risk_penalty, run_hook, run_monte_carlo_chunk, run_plugins_for_event, sample_pert_beta, sample_task_hours, schedule_next, simulate_schedule, stringifyFrontmatter, stringifyYamlContent, task_effort_hours, topological_sort, transition, transitive_dependencies, transitive_dependents, type_weight, updateItem, urgency_weight, validate, validateReferential, validateRepo, validateSemantic, validateStructural, validateTransition, validate_graph, validate_transition, writeTask, writeYamlFile, write_schema_version };
1488
+ export { type AIAgent, type AIResource, type AgentSpec, type AllocationResult, ArtifactType, type AuthConfig, type AuthPolicy, type AutoTransitionResult, type AvailabilityWindow, type BacklogItem, type BacklogItemData, COOP_EVENT_TYPES, CURRENT_SCHEMA_VERSION, type CapacityLedger, type ComparisonResult, type ComparisonRow, type ComputeNode, type ComputeResource, type CoopConfig, type CoopEvent, CoopEventEmitter, type CoopEventType, type CoopProjectRef, type CoopWorkspaceConfig, type CreateItemParams, type CriticalPathResult, type CriticalPathTaskMetrics, DEFAULT_SCORE_WEIGHTS, type Delivery, type DeliveryAtRisk, type DeliveryBudget, type DeliveryCommitted, type DeliveryGovernance, type DeliveryRisk, type DeliveryRiskType, type DeliveryScope, DeliveryStatus, type DetectedDeliveryRisk, type EffectiveCapacity, type EventByType, type ExecutionConstraints, type ExecutionPermissions, ExecutorType, type ExternalDependencyRef, type ExternalDependencyResolution, type ExternalDependencyResolutionStatus, type ExternalDependencyResolverOptions, type ExternalRepoConfig, type FeasibilityResult, type FeasibilityRisk, type FeasibilityStatus, type FeasibilitySummary, type FilterSpec, type FrontmatterParseResult, type GraphCycleDetected, type GraphValidationContext, type GraphValidationResult, type HookRunResult, type HumanMember, type HumanResource, ITEM_STATUSES, ITEM_TYPES, type Idea, IdeaStatus, IndexManager, type IndexStatus, type ItemLinks, type ItemStatus, type ItemType, MIGRATIONS, type MigrateRepositoryOptions, type MigrationContext, type MigrationDefinition, type MigrationReport, type MonteCarloHistogramBucket, type MonteCarloOptions, type MonteCarloResult, type MonteCarloWorkerPayload, type ParsedDelivery, type ParsedIdea, type ParsedTask, type Permission, type PermissionContext, type PluginAction, type PluginActionConsole, type PluginActionGitHubPr, type PluginActionHandler, type PluginActionHandlerResult, type PluginActionWebhook, type PluginManifest, type PluginRunOptions, type PluginRunRecord, type PluginTrigger, type PolicyAction, type ReadinessComputation, type ReadinessPartitions, type ReadinessState, type ReadinessTransitionEvent, type ReadinessWarning, type ReferentialValidationContext, type RepoConfig, type RepoState, type ResourceProfile, RiskLevel, type Role, type Run, type RunCompleted, type RunFailed, type RunResourcesConsumed, type RunStarted, RunStatus, type RunStepResult, RunStepStatus, RunbookAction, type RunbookStep, type ScheduleOptions, type ScoreContext, type ScoredTask, type SemanticValidationContext, type SimulationResult, type StructuralValidationContext, type Task, type TaskAssigned, TaskComplexity, type TaskComputed, type TaskCore, type TaskCreated, TaskDeterminism, type TaskEstimate, type TaskEstimation, type TaskExecution, type TaskGovernance, type TaskGraph, type TaskPlanning, TaskPriority, type TaskResources, type TaskScheduleEntry, TaskStatus, type TaskTransitioned, type TaskTransitionedEvent, TaskType, type Track, type TrackUtilization, type TrackWip, type TransitionContext, type TransitionResult, type TransitionValidationContext, type UpdateItemParams, VALID_TASK_TRANSITIONS, VALID_TRANSITIONS, type ValidationContext, type ValidationError, type ValidationLevel, type ValidationResult, type VelocityMetrics, type VelocityPoint, type VelocityTrend, type WhatIfBaseline, type WhatIfModification, type WriteTaskOptions, allocate, allocate_ai, allocate_ai_tokens, analyze_feasibility, analyze_what_if, build_capacity_ledger, build_graph, check_blocked, check_permission, check_unblocked, check_wip, completeItem, complexity_penalty, compute_all_readiness, compute_critical_path, compute_readiness, compute_readiness_with_corrections, compute_score, compute_velocity, coop_project_config_path, coop_project_root, coop_projects_dir, coop_workspace_config_path, coop_workspace_dir, createItem, create_seeded_rng, critical_path_weight, deleteItem, dependency_unlock_weight, detect_cycle, detect_delivery_risks, determinism_weight, effective_weekly_hours, effort_or_default, ensureCoopLayout, ensure_workspace_layout, executor_fit_weight, external_dependencies_for_task, extract_subgraph, findRepoRoot, find_external_dependencies, getItemById, get_remaining_tokens, get_user_role, has_legacy_project_layout, has_v2_projects_layout, is_external_dependency, is_project_initialized, list_projects, loadState, load_auth_config, load_completed_runs, load_graph, load_plugins, migrate_repository, migrate_task, monte_carlo_forecast, parseDeliveryContent, parseDeliveryFile, parseFrontmatterContent, parseFrontmatterFile, parseIdeaContent, parseIdeaFile, parseTaskContent, parseTaskFile, parseYamlContent, parseYamlFile, parse_external_dependency, partition_by_readiness, pert_hours, pert_stddev, priority_weight, queryItems, read_project_config, read_schema_version, read_workspace_config, renderAgentPrompt, repo_default_project_id, repo_default_project_name, resolve_external_dependencies, resolve_project, risk_penalty, run_hook, run_monte_carlo_chunk, run_plugins_for_event, sample_pert_beta, sample_task_hours, schedule_next, simulate_schedule, stringifyFrontmatter, stringifyYamlContent, task_effort_hours, topological_sort, transition, transitive_dependencies, transitive_dependents, type_weight, updateItem, urgency_weight, validate, validateReferential, validateRepo, validateSemantic, validateStructural, validateTransition, validate_graph, validate_transition, writeTask, writeYamlFile, write_schema_version, write_workspace_config };
package/dist/index.js CHANGED
@@ -3791,9 +3791,131 @@ function validate(task, context = {}) {
3791
3791
  };
3792
3792
  }
3793
3793
 
3794
- // src/core.ts
3794
+ // src/workspace.ts
3795
3795
  import fs13 from "fs";
3796
3796
  import path9 from "path";
3797
+ var COOP_DIR_NAME = ".coop";
3798
+ function sanitizeProjectId(value, fallback) {
3799
+ const normalized = value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").replace(/-+/g, "-");
3800
+ return normalized || fallback;
3801
+ }
3802
+ function coop_workspace_dir(repoRoot) {
3803
+ return path9.join(path9.resolve(repoRoot), COOP_DIR_NAME);
3804
+ }
3805
+ function coop_projects_dir(repoRoot) {
3806
+ return path9.join(coop_workspace_dir(repoRoot), "projects");
3807
+ }
3808
+ function coop_workspace_config_path(repoRoot) {
3809
+ return path9.join(coop_workspace_dir(repoRoot), "config.yml");
3810
+ }
3811
+ function coop_project_root(repoRoot, projectId) {
3812
+ return path9.join(coop_projects_dir(repoRoot), projectId);
3813
+ }
3814
+ function coop_project_config_path(projectRoot) {
3815
+ return path9.join(projectRoot, "config.yml");
3816
+ }
3817
+ function repo_default_project_id(repoRoot) {
3818
+ return sanitizeProjectId(path9.basename(path9.resolve(repoRoot)), "workspace");
3819
+ }
3820
+ function repo_default_project_name(repoRoot) {
3821
+ const base = path9.basename(path9.resolve(repoRoot)).trim();
3822
+ return base || "COOP Workspace";
3823
+ }
3824
+ function has_v2_projects_layout(repoRoot) {
3825
+ return fs13.existsSync(coop_projects_dir(repoRoot));
3826
+ }
3827
+ function has_legacy_project_layout(repoRoot) {
3828
+ const workspaceDir = coop_workspace_dir(repoRoot);
3829
+ return fs13.existsSync(workspaceDir) && fs13.existsSync(path9.join(workspaceDir, "config.yml")) && !fs13.existsSync(coop_projects_dir(repoRoot));
3830
+ }
3831
+ function read_workspace_config(repoRoot) {
3832
+ const configPath = coop_workspace_config_path(repoRoot);
3833
+ if (!fs13.existsSync(configPath) || has_legacy_project_layout(repoRoot)) {
3834
+ return { version: 2 };
3835
+ }
3836
+ return parseYamlFile(configPath);
3837
+ }
3838
+ function write_workspace_config(repoRoot, config) {
3839
+ fs13.mkdirSync(coop_workspace_dir(repoRoot), { recursive: true });
3840
+ writeYamlFile(coop_workspace_config_path(repoRoot), {
3841
+ version: config.version ?? 2,
3842
+ ...config.current_project ? { current_project: config.current_project } : {}
3843
+ });
3844
+ }
3845
+ function read_project_config(projectRoot) {
3846
+ return parseYamlFile(coop_project_config_path(projectRoot));
3847
+ }
3848
+ function project_ref_from_config(repoRoot, projectRoot, layout) {
3849
+ const config = read_project_config(projectRoot);
3850
+ const repoName = repo_default_project_name(repoRoot);
3851
+ const fallbackId = repo_default_project_id(repoRoot);
3852
+ return {
3853
+ id: sanitizeProjectId(config.project?.id ?? fallbackId, fallbackId),
3854
+ name: config.project?.name?.trim() || repoName,
3855
+ aliases: Array.isArray(config.project?.aliases) ? config.project.aliases.filter((entry) => typeof entry === "string" && entry.trim().length > 0) : [],
3856
+ root: projectRoot,
3857
+ repo_root: path9.resolve(repoRoot),
3858
+ layout
3859
+ };
3860
+ }
3861
+ function list_projects(repoRoot) {
3862
+ if (has_v2_projects_layout(repoRoot)) {
3863
+ const projectsDir = coop_projects_dir(repoRoot);
3864
+ return fs13.readdirSync(projectsDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => path9.join(projectsDir, entry.name)).filter((projectRoot) => fs13.existsSync(coop_project_config_path(projectRoot))).map((projectRoot) => project_ref_from_config(repoRoot, projectRoot, "v2")).sort((a, b) => a.id.localeCompare(b.id));
3865
+ }
3866
+ if (has_legacy_project_layout(repoRoot)) {
3867
+ return [project_ref_from_config(repoRoot, coop_workspace_dir(repoRoot), "legacy")];
3868
+ }
3869
+ return [];
3870
+ }
3871
+ function resolve_project(repoRoot, options = {}) {
3872
+ const projects = list_projects(repoRoot);
3873
+ const requested = options.project?.trim().toLowerCase();
3874
+ if (requested) {
3875
+ const match = projects.find(
3876
+ (project) => project.id.toLowerCase() === requested || project.name.toLowerCase() === requested || project.aliases.some((alias) => alias.toLowerCase() === requested)
3877
+ );
3878
+ if (!match) {
3879
+ throw new Error(`Project '${options.project}' not found.`);
3880
+ }
3881
+ return match;
3882
+ }
3883
+ if (projects.length === 1) {
3884
+ return projects[0];
3885
+ }
3886
+ const workspaceConfig = read_workspace_config(repoRoot);
3887
+ if (workspaceConfig.current_project) {
3888
+ const match = projects.find((project) => project.id === workspaceConfig.current_project);
3889
+ if (match) return match;
3890
+ }
3891
+ if (!options.require && projects.length === 0) {
3892
+ return {
3893
+ id: repo_default_project_id(repoRoot),
3894
+ name: repo_default_project_name(repoRoot),
3895
+ aliases: [],
3896
+ root: coop_project_root(repoRoot, repo_default_project_id(repoRoot)),
3897
+ repo_root: path9.resolve(repoRoot),
3898
+ layout: "v2"
3899
+ };
3900
+ }
3901
+ if (projects.length === 0) {
3902
+ throw new Error("No COOP project found. Run 'coop init'.");
3903
+ }
3904
+ throw new Error("Multiple COOP projects found. Pass --project <id> or run 'coop project use <id>'.");
3905
+ }
3906
+ function ensure_workspace_layout(repoRoot) {
3907
+ const workspaceDir = coop_workspace_dir(repoRoot);
3908
+ fs13.mkdirSync(workspaceDir, { recursive: true });
3909
+ fs13.mkdirSync(coop_projects_dir(repoRoot), { recursive: true });
3910
+ return workspaceDir;
3911
+ }
3912
+ function is_project_initialized(projectRoot) {
3913
+ return fs13.existsSync(coop_project_config_path(projectRoot));
3914
+ }
3915
+
3916
+ // src/core.ts
3917
+ import fs14 from "fs";
3918
+ import path10 from "path";
3797
3919
  import matter from "gray-matter";
3798
3920
 
3799
3921
  // src/types.ts
@@ -3819,17 +3941,17 @@ function toIdKey(value) {
3819
3941
  return value.trim().toUpperCase();
3820
3942
  }
3821
3943
  function repoRootByPackage(cwd) {
3822
- let current = path9.resolve(cwd);
3944
+ let current = path10.resolve(cwd);
3823
3945
  let lastWorkspaceRoot = null;
3824
3946
  while (true) {
3825
- const packageJson = path9.join(current, "package.json");
3826
- const workspaceYaml = path9.join(current, "pnpm-workspace.yaml");
3827
- if (fs13.existsSync(packageJson) && fs13.existsSync(workspaceYaml)) {
3947
+ const packageJson = path10.join(current, "package.json");
3948
+ const workspaceYaml = path10.join(current, "pnpm-workspace.yaml");
3949
+ if (fs14.existsSync(packageJson) && fs14.existsSync(workspaceYaml)) {
3828
3950
  lastWorkspaceRoot = current;
3829
- const hasCoop = fs13.existsSync(path9.join(current, COOP_DIR, "config.yml"));
3951
+ const hasCoop = fs14.existsSync(path10.join(current, COOP_DIR, "config.yml"));
3830
3952
  if (hasCoop) return current;
3831
3953
  }
3832
- const parent = path9.dirname(current);
3954
+ const parent = path10.dirname(current);
3833
3955
  if (parent === current) return lastWorkspaceRoot;
3834
3956
  current = parent;
3835
3957
  }
@@ -3838,24 +3960,24 @@ function findRepoRoot(cwd = process.cwd()) {
3838
3960
  return repoRootByPackage(cwd);
3839
3961
  }
3840
3962
  function configPathFor(rootDir, workspaceDir) {
3841
- return path9.join(rootDir, workspaceDir, "config.yml");
3963
+ return path10.join(rootDir, workspaceDir, "config.yml");
3842
3964
  }
3843
3965
  function backlogPathFor(rootDir, workspaceDir) {
3844
- return path9.join(rootDir, workspaceDir, "backlog");
3966
+ return path10.join(rootDir, workspaceDir, "backlog");
3845
3967
  }
3846
3968
  function releasesPathFor(rootDir, workspaceDir) {
3847
- return path9.join(rootDir, workspaceDir, "releases");
3969
+ return path10.join(rootDir, workspaceDir, "releases");
3848
3970
  }
3849
3971
  function detectWorkspaceDir(rootDir) {
3850
- if (fs13.existsSync(configPathFor(rootDir, COOP_DIR))) return COOP_DIR;
3851
- if (fs13.existsSync(path9.join(rootDir, COOP_DIR))) return COOP_DIR;
3972
+ if (fs14.existsSync(configPathFor(rootDir, COOP_DIR))) return COOP_DIR;
3973
+ if (fs14.existsSync(path10.join(rootDir, COOP_DIR))) return COOP_DIR;
3852
3974
  return null;
3853
3975
  }
3854
3976
  function preferredWorkspaceDir(rootDir) {
3855
3977
  return detectWorkspaceDir(rootDir) ?? COOP_DIR;
3856
3978
  }
3857
3979
  function missingConfigError(rootDir) {
3858
- const coopConfig = path9.relative(rootDir, configPathFor(rootDir, COOP_DIR));
3980
+ const coopConfig = path10.relative(rootDir, configPathFor(rootDir, COOP_DIR));
3859
3981
  return new Error(`COOP config missing at ${coopConfig}. Run: coop init`);
3860
3982
  }
3861
3983
  function parseConfig(raw) {
@@ -3901,10 +4023,10 @@ function configToString(config) {
3901
4023
  return lines.join("\n");
3902
4024
  }
3903
4025
  function toPortablePath(value) {
3904
- return value.split(path9.sep).join("/");
4026
+ return value.split(path10.sep).join("/");
3905
4027
  }
3906
4028
  function ensureReleasesDir(rootDir, workspaceDir) {
3907
- fs13.mkdirSync(releasesPathFor(rootDir, workspaceDir), { recursive: true });
4029
+ fs14.mkdirSync(releasesPathFor(rootDir, workspaceDir), { recursive: true });
3908
4030
  }
3909
4031
  function releaseHeader(date) {
3910
4032
  return `## ${date}`;
@@ -3926,12 +4048,12 @@ function appendReleaseEntry(rootDir, workspaceDir, item, previousStatus, nextSta
3926
4048
  ensureReleasesDir(rootDir, workspaceDir);
3927
4049
  const now = /* @__PURE__ */ new Date();
3928
4050
  const date = now.toISOString().slice(0, 10);
3929
- const releasePath = path9.join(releasesPathFor(rootDir, workspaceDir), `${date}.md`);
4051
+ const releasePath = path10.join(releasesPathFor(rootDir, workspaceDir), `${date}.md`);
3930
4052
  const heading = "# COOP Release Notes";
3931
4053
  const dayHeader = releaseHeader(date);
3932
4054
  const entry = releaseEntryLine(item, previousStatus, nextStatus);
3933
- if (!fs13.existsSync(releasePath)) {
3934
- fs13.writeFileSync(
4055
+ if (!fs14.existsSync(releasePath)) {
4056
+ fs14.writeFileSync(
3935
4057
  releasePath,
3936
4058
  [
3937
4059
  `${heading}
@@ -3944,10 +4066,10 @@ function appendReleaseEntry(rootDir, workspaceDir, item, previousStatus, nextSta
3944
4066
  ].join("\n"),
3945
4067
  "utf8"
3946
4068
  );
3947
- return toPortablePath(path9.relative(rootDir, releasePath));
4069
+ return toPortablePath(path10.relative(rootDir, releasePath));
3948
4070
  }
3949
- const existing = fs13.readFileSync(releasePath, "utf8");
3950
- if (hasReleaseEntry(existing, item.id)) return toPortablePath(path9.relative(rootDir, releasePath));
4071
+ const existing = fs14.readFileSync(releasePath, "utf8");
4072
+ if (hasReleaseEntry(existing, item.id)) return toPortablePath(path10.relative(rootDir, releasePath));
3951
4073
  let nextContent = existing;
3952
4074
  if (!existing.includes(`## ${date}`)) {
3953
4075
  if (!nextContent.endsWith("\n")) nextContent += "\n";
@@ -3957,9 +4079,9 @@ function appendReleaseEntry(rootDir, workspaceDir, item, previousStatus, nextSta
3957
4079
  if (!nextContent.endsWith("\n")) nextContent += "\n";
3958
4080
  nextContent += `${entry}
3959
4081
  `;
3960
- fs13.writeFileSync(releasePath, `${nextContent}
4082
+ fs14.writeFileSync(releasePath, `${nextContent}
3961
4083
  `, "utf8");
3962
- return toPortablePath(path9.relative(rootDir, releasePath));
4084
+ return toPortablePath(path10.relative(rootDir, releasePath));
3963
4085
  }
3964
4086
  function completeItem(rootDir, id) {
3965
4087
  const state = loadState(rootDir);
@@ -4037,28 +4159,28 @@ function validateAndNormalize(data, sourceFile) {
4037
4159
  };
4038
4160
  }
4039
4161
  function parseItem(filePath, rootDir) {
4040
- const raw = fs13.readFileSync(filePath, "utf8");
4162
+ const raw = fs14.readFileSync(filePath, "utf8");
4041
4163
  const parsed = matter(raw);
4042
- const data = validateAndNormalize(parsed.data, path9.relative(rootDir, filePath));
4164
+ const data = validateAndNormalize(parsed.data, path10.relative(rootDir, filePath));
4043
4165
  return {
4044
4166
  ...data,
4045
4167
  body: parsed.content || "",
4046
- filePath: path9.relative(rootDir, filePath)
4168
+ filePath: path10.relative(rootDir, filePath)
4047
4169
  };
4048
4170
  }
4049
4171
  function walk(dir) {
4050
4172
  const out = [];
4051
- if (!fs13.existsSync(dir)) return out;
4052
- const entries = fs13.readdirSync(dir, { withFileTypes: true });
4173
+ if (!fs14.existsSync(dir)) return out;
4174
+ const entries = fs14.readdirSync(dir, { withFileTypes: true });
4053
4175
  for (const entry of entries) {
4054
- const file = path9.join(dir, entry.name);
4176
+ const file = path10.join(dir, entry.name);
4055
4177
  if (entry.isDirectory()) out.push(...walk(file));
4056
4178
  if (entry.isFile() && file.endsWith(".md")) out.push(file);
4057
4179
  }
4058
4180
  return out;
4059
4181
  }
4060
4182
  function itemPath(type, id, rootDir, workspaceDir) {
4061
- return path9.join(backlogPathFor(rootDir, workspaceDir), ITEM_DIRS[type], `${id}.md`);
4183
+ return path10.join(backlogPathFor(rootDir, workspaceDir), ITEM_DIRS[type], `${id}.md`);
4062
4184
  }
4063
4185
  function normalizeFrontmatterValue(value) {
4064
4186
  if (value == null) return void 0;
@@ -4145,30 +4267,30 @@ function nextGeneratedId(config, title, existing) {
4145
4267
  }
4146
4268
  function ensureCoopLayout(rootDir) {
4147
4269
  const workspaceDir = preferredWorkspaceDir(rootDir);
4148
- const root = path9.join(rootDir, workspaceDir);
4149
- fs13.mkdirSync(root, { recursive: true });
4150
- fs13.mkdirSync(path9.join(root, "releases"), { recursive: true });
4151
- fs13.mkdirSync(path9.join(root, "plans"), { recursive: true });
4152
- fs13.mkdirSync(path9.join(root, "views"), { recursive: true });
4153
- fs13.mkdirSync(path9.join(root, "templates"), { recursive: true });
4270
+ const root = path10.join(rootDir, workspaceDir);
4271
+ fs14.mkdirSync(root, { recursive: true });
4272
+ fs14.mkdirSync(path10.join(root, "releases"), { recursive: true });
4273
+ fs14.mkdirSync(path10.join(root, "plans"), { recursive: true });
4274
+ fs14.mkdirSync(path10.join(root, "views"), { recursive: true });
4275
+ fs14.mkdirSync(path10.join(root, "templates"), { recursive: true });
4154
4276
  for (const dir of Object.values(ITEM_DIRS)) {
4155
- fs13.mkdirSync(path9.join(root, "backlog", dir), { recursive: true });
4277
+ fs14.mkdirSync(path10.join(root, "backlog", dir), { recursive: true });
4156
4278
  }
4157
- const configFile = path9.join(root, "config.yml");
4158
- if (!fs13.existsSync(configFile)) {
4159
- fs13.writeFileSync(configFile, configToString(DEFAULT_CONFIG2), "utf8");
4279
+ const configFile = path10.join(root, "config.yml");
4280
+ if (!fs14.existsSync(configFile)) {
4281
+ fs14.writeFileSync(configFile, configToString(DEFAULT_CONFIG2), "utf8");
4160
4282
  }
4161
4283
  }
4162
4284
  function loadState(rootDir) {
4163
4285
  const workspaceDir = detectWorkspaceDir(rootDir);
4164
4286
  if (!workspaceDir) throw missingConfigError(rootDir);
4165
4287
  const configPath = configPathFor(rootDir, workspaceDir);
4166
- if (!fs13.existsSync(configPath)) throw missingConfigError(rootDir);
4167
- const config = parseConfig(fs13.readFileSync(configPath, "utf8"));
4288
+ if (!fs14.existsSync(configPath)) throw missingConfigError(rootDir);
4289
+ const config = parseConfig(fs14.readFileSync(configPath, "utf8"));
4168
4290
  const items = [];
4169
4291
  const itemsById = /* @__PURE__ */ new Map();
4170
4292
  for (const type of ITEM_TYPES) {
4171
- const dir = path9.join(backlogPathFor(rootDir, workspaceDir), ITEM_DIRS[type]);
4293
+ const dir = path10.join(backlogPathFor(rootDir, workspaceDir), ITEM_DIRS[type]);
4172
4294
  const files = walk(dir);
4173
4295
  for (const file of files) {
4174
4296
  const item = parseItem(file, rootDir);
@@ -4252,21 +4374,21 @@ function createItem(rootDir, params) {
4252
4374
  parent_id: params.parent_id
4253
4375
  };
4254
4376
  const itemPathName = itemPath(params.type, item.id, rootDir, state.workspaceDir);
4255
- fs13.writeFileSync(itemPathName, serialize(item, params.body || ""), "utf8");
4377
+ fs14.writeFileSync(itemPathName, serialize(item, params.body || ""), "utf8");
4256
4378
  if ((config.id_strategy ?? "text") === "counter") {
4257
4379
  const numericMatch = /-(\d+)$/.exec(item.id);
4258
4380
  if (numericMatch) {
4259
4381
  const numericValue = Number(numericMatch[1]);
4260
4382
  if (Number.isInteger(numericValue) && numericValue >= (config.next_id ?? 1)) {
4261
4383
  config.next_id = numericValue + 1;
4262
- fs13.writeFileSync(configPathFor(rootDir, state.workspaceDir), configToString(config), "utf8");
4384
+ fs14.writeFileSync(configPathFor(rootDir, state.workspaceDir), configToString(config), "utf8");
4263
4385
  }
4264
4386
  }
4265
4387
  }
4266
4388
  return {
4267
4389
  ...item,
4268
4390
  body: params.body || "",
4269
- filePath: path9.relative(rootDir, itemPathName)
4391
+ filePath: path10.relative(rootDir, itemPathName)
4270
4392
  };
4271
4393
  }
4272
4394
  function updateItem(rootDir, id, patch) {
@@ -4292,8 +4414,8 @@ function updateItem(rootDir, id, patch) {
4292
4414
  };
4293
4415
  if (!ITEM_TYPES.includes(next.type)) throw new Error(`Unknown type ${next.type}.`);
4294
4416
  if (!ITEM_STATUSES.includes(next.status)) throw new Error(`Unknown status ${next.status}.`);
4295
- const filePath = path9.join(rootDir, existing.filePath);
4296
- fs13.writeFileSync(filePath, serialize(next, patch.body || existing.body), "utf8");
4417
+ const filePath = path10.join(rootDir, existing.filePath);
4418
+ fs14.writeFileSync(filePath, serialize(next, patch.body || existing.body), "utf8");
4297
4419
  return {
4298
4420
  ...next,
4299
4421
  body: patch.body || existing.body,
@@ -4309,7 +4431,7 @@ function deleteItem(rootDir, id) {
4309
4431
  if (children.length > 0) {
4310
4432
  throw new Error(`Cannot delete ${existing.id} because it has ${children.length} child item(s). Remove children first.`);
4311
4433
  }
4312
- fs13.unlinkSync(path9.join(rootDir, existing.filePath));
4434
+ fs14.unlinkSync(path10.join(rootDir, existing.filePath));
4313
4435
  return existing;
4314
4436
  }
4315
4437
  function renderAgentPrompt(item) {
@@ -4348,8 +4470,8 @@ function validateRepo(rootDir) {
4348
4470
  const errors = [];
4349
4471
  const warnings = [];
4350
4472
  const workspaceDir = detectWorkspaceDir(rootDir);
4351
- if (!workspaceDir || !fs13.existsSync(configPathFor(rootDir, workspaceDir))) {
4352
- errors.push("Missing .coop/config.yml. Run coop init first.");
4473
+ if (!workspaceDir || !fs14.existsSync(configPathFor(rootDir, workspaceDir))) {
4474
+ errors.push("Missing COOP config. Run coop init first.");
4353
4475
  return { valid: false, errors, warnings };
4354
4476
  }
4355
4477
  let state;
@@ -4422,6 +4544,11 @@ export {
4422
4544
  compute_readiness_with_corrections,
4423
4545
  compute_score,
4424
4546
  compute_velocity,
4547
+ coop_project_config_path,
4548
+ coop_project_root,
4549
+ coop_projects_dir,
4550
+ coop_workspace_config_path,
4551
+ coop_workspace_dir,
4425
4552
  createItem,
4426
4553
  create_seeded_rng,
4427
4554
  critical_path_weight,
@@ -4433,6 +4560,7 @@ export {
4433
4560
  effective_weekly_hours,
4434
4561
  effort_or_default,
4435
4562
  ensureCoopLayout,
4563
+ ensure_workspace_layout,
4436
4564
  executor_fit_weight,
4437
4565
  external_dependencies_for_task,
4438
4566
  extract_subgraph,
@@ -4441,7 +4569,11 @@ export {
4441
4569
  getItemById,
4442
4570
  get_remaining_tokens,
4443
4571
  get_user_role,
4572
+ has_legacy_project_layout,
4573
+ has_v2_projects_layout,
4444
4574
  is_external_dependency,
4575
+ is_project_initialized,
4576
+ list_projects,
4445
4577
  loadState,
4446
4578
  load_auth_config,
4447
4579
  load_completed_runs,
@@ -4466,9 +4598,14 @@ export {
4466
4598
  pert_stddev,
4467
4599
  priority_weight,
4468
4600
  queryItems,
4601
+ read_project_config,
4469
4602
  read_schema_version,
4603
+ read_workspace_config,
4470
4604
  renderAgentPrompt,
4605
+ repo_default_project_id,
4606
+ repo_default_project_name,
4471
4607
  resolve_external_dependencies,
4608
+ resolve_project,
4472
4609
  risk_penalty,
4473
4610
  run_hook,
4474
4611
  run_monte_carlo_chunk,
@@ -4497,5 +4634,6 @@ export {
4497
4634
  validate_transition,
4498
4635
  writeTask,
4499
4636
  writeYamlFile,
4500
- write_schema_version
4637
+ write_schema_version,
4638
+ write_workspace_config
4501
4639
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@kitsy/coop-core",
3
3
  "description": "Core models, parser, validator, graph, and planning engine for COOP.",
4
- "version": "1.0.0",
4
+ "version": "2.0.0",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "publishConfig": {