codecartographer-pi 0.17.0 → 0.17.1

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.
Files changed (56) hide show
  1. package/.codecarto/GUIDE.md +1 -1
  2. package/.codecarto/findings/architecture/SKILL.md +1 -0
  3. package/.codecarto/findings/contracts/SKILL.md +1 -0
  4. package/.codecarto/findings/defect-scan/SKILL.md +15 -1
  5. package/.codecarto/findings/defect-scan/passes/01-logic-and-correctness.md +1 -1
  6. package/.codecarto/findings/defect-scan/passes/02-error-handling.md +1 -1
  7. package/.codecarto/findings/defect-scan/passes/03-concurrency-and-resources.md +1 -1
  8. package/.codecarto/findings/defect-scan/passes/04-security-and-trust.md +1 -1
  9. package/.codecarto/findings/defect-scan/passes/05-api-contract-violations.md +2 -1
  10. package/.codecarto/findings/defect-scan/passes/06-config-and-environment.md +1 -1
  11. package/.codecarto/findings/defect-scan-mechanical/SKILL.md +3 -2
  12. package/.codecarto/findings/defect-scan-semantic/SKILL.md +2 -0
  13. package/.codecarto/findings/porting/SKILL.md +2 -1
  14. package/.codecarto/findings/protocols/SKILL.md +1 -0
  15. package/.codecarto/findings/reimplementation-spec/SKILL.md +1 -1
  16. package/.codecarto/templates/architecture-map.md +1 -1
  17. package/.codecarto/templates/defect-report.md +23 -0
  18. package/.codecarto/templates/mechanical-defects.md +22 -0
  19. package/.codecarto/templates/reverse-engineering-bundle.md +2 -2
  20. package/.codecarto/templates/semantic-defects.md +26 -0
  21. package/.codecarto/workflow/VALIDATE.md +1 -1
  22. package/.codecarto/workflow/pipeline-defect-scan.yaml +2 -0
  23. package/.codecarto/workflow/pipeline-full-with-audit.yaml +3 -1
  24. package/.codecarto/workflow/pipeline-full-with-deep-audit.yaml +5 -1
  25. package/.codecarto/workflow/pipeline-scout-first.yaml +5 -1
  26. package/.codecarto/workflow/scaffold-version.yaml +1 -1
  27. package/README.md +4 -4
  28. package/agent-skill/codecartographer/references/deep-audit-synthesis.md +4 -1
  29. package/agent-skill/codecartographer/references/library.md +1 -1
  30. package/agent-skill/codecartographer/references/orchestration.md +1 -1
  31. package/dist/core/amendment.js +2 -2
  32. package/dist/core/completion.d.ts +5 -0
  33. package/dist/core/completion.js +18 -2
  34. package/dist/core/dashboard.js +5 -3
  35. package/dist/core/findings.d.ts +59 -0
  36. package/dist/core/findings.js +145 -0
  37. package/dist/core/index.d.ts +1 -0
  38. package/dist/core/index.js +1 -0
  39. package/dist/core/library.d.ts +66 -1
  40. package/dist/core/library.js +161 -8
  41. package/dist/core/pipeline.js +15 -0
  42. package/dist/core/prompts.js +1 -1
  43. package/dist/core/status.js +14 -6
  44. package/dist/core/types.d.ts +6 -0
  45. package/dist/core/utils.d.ts +14 -0
  46. package/dist/core/utils.js +30 -0
  47. package/dist/core/workspace.js +11 -18
  48. package/dist/core/yaml.js +19 -4
  49. package/dist/extensions/codecarto/auto-runner.d.ts +2 -0
  50. package/dist/extensions/codecarto/broadside-flags.d.ts +7 -2
  51. package/dist/extensions/codecarto/broadside-flags.js +22 -9
  52. package/dist/extensions/codecarto/dashboard-narrator.js +1 -1
  53. package/dist/extensions/codecarto/index.js +40 -16
  54. package/dist/extensions/codecarto/phase-compaction.js +4 -0
  55. package/dist/mcp-server/server.js +65 -7
  56. package/package.json +2 -2
@@ -17,7 +17,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
17
17
  import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from "@modelcontextprotocol/sdk/types.js";
18
18
  import { mkdir, readFile, readdir, rename, writeFile } from "node:fs/promises";
19
19
  import { basename, isAbsolute, join } from "node:path";
20
- import { buildPhasePrompt, buildSkillPrompt, buildValidationSummary, BROADSIDE_DIR, broadsideDirFor, BROADSIDE_LENS_IDS, BROADSIDE_SKILL_NAME, canonicalPath, copyPackagedWorkspace, collectResultText, completeValidatedPhase, computePerPhaseTotals, computeTotals, createEmptyStatus, DEFAULT_PIPELINE_PATH, deriveSlug, discoverLibrary, describeScaffoldStaleness, estimateSubmitText, getLens, getNextEligiblePhase, getPipelineLabel, getWorkspaceState, isValidSlug, isWithinPathResolved, listBatchModels, listEntries, listGuideTopics, loadBroadsideConfig, modelsText, readGuide, listSkillNames, loadCodecartoConfig, loadUsage, loadYamlFile, normalizeForComparison, PACKAGE_VERSION, packagedWorkspaceDir, pathExists, PhasePreflightError, publishEntry, reindex as libraryReindex, refreshScaffold, resolvePhase, resolvePipelineChoice, readBroadsideSkill, runBroadsideCollect, runBroadsideStatus, runBroadsideSubmit, seedOrchestratorFiles, statusText, stringifySimpleYaml, switchPipeline, validatePhaseOutput, writeLibraryConfig, } from "../core/index.js";
20
+ import { buildPhasePrompt, buildSkillPrompt, buildValidationSummary, BROADSIDE_DIR, broadsideDirFor, BROADSIDE_LENS_IDS, BROADSIDE_SKILL_NAME, canonicalPath, copyPackagedWorkspace, collectResultText, completeValidatedPhase, computePerPhaseTotals, computeTotals, createEmptyStatus, DEFAULT_PIPELINE_PATH, deriveSlug, discoverLibrary, describeScaffoldStaleness, detectProvenanceConflicts, estimateSubmitText, getLens, getNextEligiblePhase, getPipelineLabel, getWorkspaceState, isValidSlug, isWithinPathResolved, listBatchModels, listEntries, listGuideTopics, loadBroadsideConfig, modelsText, readGuide, listSkillNames, loadCodecartoConfig, loadUsage, loadYamlFile, normalizeForComparison, PACKAGE_VERSION, packagedWorkspaceDir, pathExists, PhasePreflightError, publishEntry, reindex as libraryReindex, refreshScaffold, resolvePhase, resolvePipelineChoice, readBroadsideSkill, runBroadsideCollect, runBroadsideStatus, runBroadsideSubmit, seedOrchestratorFiles, statusText, stringifySimpleYaml, switchPipeline, validatePhaseOutput, writeLibraryConfig, } from "../core/index.js";
21
21
  import { applyAmendment } from "../core/amendment.js";
22
22
  import { appendUsageRun } from "../core/usage.js";
23
23
  import { initLibrary } from "../core/library.js";
@@ -262,7 +262,7 @@ export async function handleComplete(args) {
262
262
  if (validation.overall === "FAIL" || validation.overall === "MISSING") {
263
263
  throw new McpError(ErrorCode.InvalidRequest, `Cannot complete ${validation.phaseId}: validation is ${validation.overall}.\n${buildValidationSummary(validation).join("\n")}`);
264
264
  }
265
- const { updatedState, closeoutNotice, orchestratorCheckpoint } = await completeValidatedPhase(cwd, validation, "codecarto_complete").catch((error) => {
265
+ const { updatedState, closeoutNotice, orchestratorCheckpoint, warnings } = await completeValidatedPhase(cwd, validation, "codecarto_complete").catch((error) => {
266
266
  throw new McpError(ErrorCode.InvalidParams, error instanceof Error ? error.message : String(error));
267
267
  });
268
268
  // Record the run in the usage log (issue #100). MCP hosts execute phases in
@@ -303,6 +303,10 @@ export async function handleComplete(args) {
303
303
  lines.push(orchestratorCheckpoint);
304
304
  if (dashboardPath)
305
305
  lines.push(`Dashboard refreshed: ${dashboardPath}`);
306
+ for (const warning of validation.warnings ?? [])
307
+ lines.push(`NOTE: ${warning} Non-gating.`);
308
+ for (const warning of warnings)
309
+ lines.push(`NOTE: ${warning} Non-gating.`);
306
310
  return textResult(lines.join("\n"), {
307
311
  completedPhase: validation.phaseId,
308
312
  validation: validation.overall,
@@ -310,6 +314,7 @@ export async function handleComplete(args) {
310
314
  closeoutNotice,
311
315
  orchestratorCheckpoint,
312
316
  dashboardPath,
317
+ warnings: [...(validation.warnings ?? []), ...warnings],
313
318
  });
314
319
  }
315
320
  export async function handleSkill(args) {
@@ -472,6 +477,33 @@ async function resolveDefaultsFromWorkspace(cwd, overrides) {
472
477
  }
473
478
  return { pipeline, namespace };
474
479
  }
480
+ // ---------- library provenance reporting ----------
481
+ function conflictEntryLabel(conflict) {
482
+ return conflict.namespace ? `${conflict.namespace}/${conflict.slug}` : conflict.slug;
483
+ }
484
+ /**
485
+ * The report block appended to the list and reindex text results when an
486
+ * entry's versions disagree about source_repo (#148). Empty when there is
487
+ * nothing to report, so a healthy library's output does not change. Repair
488
+ * is manual by design: the framework does not rename or renumber versions,
489
+ * because entry paths are ABI.
490
+ */
491
+ function provenanceConflictLines(conflicts) {
492
+ if (conflicts.length === 0)
493
+ return [];
494
+ const lines = [
495
+ "",
496
+ `Provenance conflicts — ${conflicts.length} ${conflicts.length === 1 ? "entry" : "entries"} whose versions disagree about source_repo:`,
497
+ ];
498
+ for (const conflict of conflicts) {
499
+ const others = conflict.disagreeing_versions
500
+ .map((v) => `v${v.version} records ${v.source_repo}`)
501
+ .join("; ");
502
+ lines.push(` ${conflictEntryLabel(conflict)}: the index advertises ${conflict.source_repo} (v${conflict.latest_version}), but ${others}.`);
503
+ }
504
+ lines.push("These entries were merged by a slug collision before publish refused cross-project appends, so each version history spans more than one codebase (a repository that genuinely moved and was re-published with allow_source_repo_change leaves the same shape).", "Repair is manual: split the entry by hand — the framework does not rename or renumber versions, because entry paths are ABI.");
505
+ return lines;
506
+ }
475
507
  // ---------- library handlers ----------
476
508
  export async function handlePublish(args) {
477
509
  const libraryPath = await resolveLibraryPath(args);
@@ -536,6 +568,7 @@ export async function handlePublish(args) {
536
568
  }, {
537
569
  forceNewVersion: args.force_new_version === true,
538
570
  allowSourceRepoChange: args.allow_source_repo_change === true,
571
+ allowConfidentialityMismatch: args.allow_confidentiality_mismatch === true,
539
572
  });
540
573
  const lines = [
541
574
  `Published ${result.namespace ? `${result.namespace}/` : ""}${result.slug} v${result.version} to ${libraryPath}`,
@@ -567,6 +600,10 @@ export async function handleLibraryList(args) {
567
600
  if (typeof args.source_repo === "string" && args.source_repo !== "")
568
601
  filter.source_repo = args.source_repo;
569
602
  const entries = await listEntries(libraryPath, filter);
603
+ // Computed over the listed entries only, read-only: one readdir per entry
604
+ // plus one small metadata read per older version.
605
+ const conflicts = await detectProvenanceConflicts(libraryPath, entries);
606
+ const conflicted = new Set(conflicts.map((c) => `${c.namespace ?? ""}/${c.slug}`));
570
607
  const summary = entries.length === 0
571
608
  ? `No entries match the filter in ${libraryPath}.`
572
609
  : [
@@ -574,8 +611,10 @@ export async function handleLibraryList(args) {
574
611
  ...entries.map((e) => {
575
612
  const ns = e.namespace ? `${e.namespace}/` : "";
576
613
  const tags = e.tags.length > 0 ? ` [${e.tags.slice(0, 4).join(", ")}${e.tags.length > 4 ? ", ..." : ""}]` : "";
577
- return ` ${ns}${e.slug} v${e.latest_version} — ${e.headline}${tags}`;
614
+ const flag = conflicted.has(`${e.namespace ?? ""}/${e.slug}`) ? " — PROVENANCE CONFLICT (see below)" : "";
615
+ return ` ${ns}${e.slug} v${e.latest_version} — ${e.headline}${tags}${flag}`;
578
616
  }),
617
+ ...provenanceConflictLines(conflicts),
579
618
  ].join("\n");
580
619
  return textResult(summary, {
581
620
  libraryPath,
@@ -583,6 +622,7 @@ export async function handleLibraryList(args) {
583
622
  namespaced: marker.namespaced,
584
623
  count: entries.length,
585
624
  entries,
625
+ provenance_conflicts: conflicts,
586
626
  });
587
627
  }
588
628
  export async function handleLibraryReindex(args) {
@@ -593,17 +633,27 @@ export async function handleLibraryReindex(args) {
593
633
  }
594
634
  const index = await libraryReindex(libraryPath);
595
635
  const namespaces = index.namespaces.length > 0 ? index.namespaces.join(", ") : "(none)";
596
- return textResult(`Reindexed ${libraryPath}: ${index.entry_count} ${index.entry_count === 1 ? "entry" : "entries"} across namespaces [${namespaces}].`, {
636
+ return textResult([
637
+ `Reindexed ${libraryPath}: ${index.entry_count} ${index.entry_count === 1 ? "entry" : "entries"} across namespaces [${namespaces}].`,
638
+ ...provenanceConflictLines(index.provenance_conflicts),
639
+ ].join("\n"), {
597
640
  libraryPath,
598
641
  libraryName: index.library_name,
599
642
  entry_count: index.entry_count,
600
643
  namespaces: index.namespaces,
644
+ provenance_conflicts: index.provenance_conflicts,
601
645
  });
602
646
  }
603
647
  export async function handleLibraryInit(args) {
604
648
  if (!args.library_path || typeof args.library_path !== "string") {
605
649
  throw new McpError(ErrorCode.InvalidParams, "library_path is required.");
606
650
  }
651
+ // Same rule as resolveLibraryPath for the other library tools: a relative
652
+ // path would resolve against the MCP server process's cwd and then be
653
+ // persisted verbatim into the user-global config (#134).
654
+ if (!isAbsolute(args.library_path)) {
655
+ throw new McpError(ErrorCode.InvalidParams, `library_path must be absolute, got: ${args.library_path}`);
656
+ }
607
657
  const libraryPath = args.library_path;
608
658
  const namespaced = !!args.namespace;
609
659
  const result = await initLibrary(libraryPath, {
@@ -1029,7 +1079,11 @@ const TOOLS = [
1029
1079
  headline: { type: "string" },
1030
1080
  tags: { type: "array", items: { type: "string" } },
1031
1081
  capabilities: { type: "array", items: { type: "string" } },
1032
- confidentiality: { type: "string", enum: ["internal", "shared", "public"] },
1082
+ confidentiality: {
1083
+ type: "string",
1084
+ enum: ["internal", "shared", "public"],
1085
+ description: "Classification of the entry. Defaults to internal. Ordered internal < shared < public; publish refuses an entry more restricted than the library's visibility unless allow_confidentiality_mismatch is set.",
1086
+ },
1033
1087
  model_metadata: {
1034
1088
  type: "object",
1035
1089
  properties: {
@@ -1046,13 +1100,17 @@ const TOOLS = [
1046
1100
  type: "boolean",
1047
1101
  description: "Permit publishing when the target entry already records a different source_repo. Off by default, because a mismatch usually means two projects derived the same slug and the spec would land in the wrong version history. Set only when the repository itself moved.",
1048
1102
  },
1103
+ allow_confidentiality_mismatch: {
1104
+ type: "boolean",
1105
+ description: "Permit publishing when the entry's confidentiality is more restricted than the library's visibility (internal < shared < public: an internal entry into a shared or public library, a shared entry into a public one). Off by default, because that direction exposes the spec to everyone the library reaches. An omitted confidentiality counts as internal. Set only when the exposure is intended; the recorded confidentiality is not changed.",
1106
+ },
1049
1107
  },
1050
1108
  required: ["source_repo", "headline"],
1051
1109
  },
1052
1110
  },
1053
1111
  {
1054
1112
  name: "codecarto_library_list",
1055
- description: "List entries in a CodeCartographer library, optionally filtered by namespace, tag, slug, or source_repo. The library is identified by library_path (absolute) or by cwd's config.yaml.",
1113
+ description: "List entries in a CodeCartographer library, optionally filtered by namespace, tag, slug, or source_repo. The library is identified by library_path (absolute) or by cwd's config.yaml. Flags entries whose versions disagree about source_repo (merged by a slug collision before publish refused cross-project appends); repair is manual.",
1056
1114
  inputSchema: {
1057
1115
  type: "object",
1058
1116
  properties: {
@@ -1067,7 +1125,7 @@ const TOOLS = [
1067
1125
  },
1068
1126
  {
1069
1127
  name: "codecarto_library_reindex",
1070
- description: "Regenerate index.yaml and INDEX.md for a CodeCartographer library from filesystem state. Use after manual edits or to resolve a git merge conflict on index.yaml.",
1128
+ description: "Regenerate index.yaml and INDEX.md for a CodeCartographer library from filesystem state. Use after manual edits or to resolve a git merge conflict on index.yaml. Also reports entries whose versions disagree about source_repo (merged by a slug collision before publish refused cross-project appends); the index files are not changed and repair is manual.",
1071
1129
  inputSchema: {
1072
1130
  type: "object",
1073
1131
  properties: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codecartographer-pi",
3
- "version": "0.17.0",
3
+ "version": "0.17.1",
4
4
  "mcpName": "io.github.HuginnIndustries/codecartographer",
5
5
  "description": "Turn an unfamiliar codebase into a validated reimplementation spec, then synthesize confirmed specs and a product vision into a traceable plan.",
6
6
  "type": "module",
@@ -64,7 +64,7 @@
64
64
  "@modelcontextprotocol/sdk": "^1.29.0"
65
65
  },
66
66
  "peerDependencies": {
67
- "@earendil-works/pi-coding-agent": "^0.80.10",
67
+ "@earendil-works/pi-coding-agent": ">=0.84.0",
68
68
  "@sinclair/typebox": "*"
69
69
  },
70
70
  "pi": {