@amistio/cli 0.1.21 → 0.1.23

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 CHANGED
@@ -9,7 +9,7 @@ npm install -g @amistio/cli
9
9
  amistio --help
10
10
  ```
11
11
 
12
- The package install only installs the `amistio` command. Repository cloning, project pairing, credential storage, sync watching, and runner execution happen only when the user explicitly runs commands such as `amistio bootstrap`, `amistio import`, `amistio pair`, `amistio sync watch`, or `amistio run --watch`. When the app copies a personal project into an organization, the CLI command syntax stays the same; create the org-scoped code and run `amistio import <code>` from the intended local checkout.
12
+ The package install only installs the `amistio` command. Repository cloning, project pairing, credential storage, sync watching, and runner execution happen only when the user explicitly runs commands such as `amistio bootstrap`, `amistio import`, `amistio pair`, `amistio sync watch`, or `amistio run --watch`. When the app copies a personal project into an organization, the CLI command syntax stays the same; create the org-scoped code and run `amistio import <code>` from the intended local checkout. Import scans repo-local project-brain docs plus recognized repo-local IDE, harness, and local AI tool memory or instruction files such as `AGENTS.md`, `.github/copilot-instructions.md`, `.cursor/rules/*.mdc`, `.windsurfrules`, and `memories/*.md`; durable Amistio memory belongs in `docs/memory/`, and global plugin or harness memory outside the repository is not scanned by default.
13
13
 
14
14
  Runner lifecycle controls in the web app, such as update, restart, and remove, apply only to the runner paired by that user unless the active organization role is an admin role. The runner API binds command polling, command status, logs, activity, and tool sessions to the local runner credential that produced them.
15
15
 
package/dist/index.js CHANGED
@@ -4570,6 +4570,14 @@ var projectContextTopLevelWarningMaxLength = 600;
4570
4570
  var validProjectContextSliceKinds = /* @__PURE__ */ new Set(["overview", "architecture", "domain", "data", "api", "frontend", "backend", "cli", "workflow", "operations", "security", "testing", "unknown"]);
4571
4571
  var validProjectContextEntityTypes = /* @__PURE__ */ new Set(["project", "system", "component", "domain", "tool", "decision", "feature", "risk", "team", "workflow", "unknown"]);
4572
4572
  var validProjectContextRelationTypes = /* @__PURE__ */ new Set(["uses", "depends_on", "decides", "supersedes", "touches", "blocks", "implements", "mentions"]);
4573
+ var canonicalProjectContextCitationSources = /* @__PURE__ */ new Map([
4574
+ ["projectbrain", "projectBrain"],
4575
+ ["approvedbrain", "projectBrain"],
4576
+ ["approvedprojectbrain", "projectBrain"],
4577
+ ["localsource", "localSource"],
4578
+ ["runnerstate", "runnerState"],
4579
+ ["mixed", "mixed"]
4580
+ ]);
4573
4581
  function createImplementationVerificationPrompt(workItem) {
4574
4582
  return [
4575
4583
  "# Amistio Implementation Verification",
@@ -4727,6 +4735,7 @@ function createProjectContextRefreshPrompt(workItem, context) {
4727
4735
  "- Use only these exact singular slice kind values: overview, architecture, domain, data, api, frontend, backend, cli, workflow, operations, security, testing, unknown.",
4728
4736
  "- Capture entities and relations that explain how the app is put together and where future work should look first.",
4729
4737
  "- Prefer summaries, repository-relative paths, short citations, tags, and freshness status over raw source excerpts.",
4738
+ "- Use only these exact citation source values: projectBrain, localSource, runnerState, mixed. Approved project-brain records use projectBrain, not approvedBrain.",
4730
4739
  "- Mark stale or missing areas explicitly instead of guessing.",
4731
4740
  "- Keep coverage.missingAreas entries concise noun phrases under 160 characters.",
4732
4741
  "- Keep coverage.warnings and verificationPlan entries under 300 characters; keep top-level warnings under 600 characters.",
@@ -5400,6 +5409,7 @@ function normalizeProjectContextCitationPaths(value, options) {
5400
5409
  return citation;
5401
5410
  }
5402
5411
  const normalizedCitation = { ...citation };
5412
+ normalizedCitation.source = normalizeProjectContextCitationSource(normalizedCitation.source);
5403
5413
  if (typeof normalizedCitation.repoPath === "string") {
5404
5414
  normalizedCitation.repoPath = normalizeProjectContextRepoPath(normalizedCitation.repoPath, options);
5405
5415
  }
@@ -5408,6 +5418,13 @@ function normalizeProjectContextCitationPaths(value, options) {
5408
5418
  }
5409
5419
  return normalized;
5410
5420
  }
5421
+ function normalizeProjectContextCitationSource(value) {
5422
+ if (typeof value !== "string") {
5423
+ return value;
5424
+ }
5425
+ const sourceKey = value.trim().replace(/[\s_-]+/g, "").toLowerCase();
5426
+ return canonicalProjectContextCitationSources.get(sourceKey) ?? value;
5427
+ }
5411
5428
  function normalizeProjectContextRepoPath(value, options) {
5412
5429
  const trimmed = value.trim();
5413
5430
  if (!trimmed || /^[A-Za-z][A-Za-z0-9+.-]*:\/\//.test(trimmed) || /^file:/i.test(trimmed) || /^[A-Za-z]:($|[^\\/])/.test(trimmed)) {
@@ -5635,6 +5652,8 @@ var controlPlaneRoots2 = ["architecture", "context", "decisions", "features", "m
5635
5652
  var excludedDirectoryNames = /* @__PURE__ */ new Set([".git", "node_modules", ".pnpm-store", ".next", "dist", "build", "coverage", ".cache", "cache", "tmp", "temp", "vendor"]);
5636
5653
  var excludedFileNames = /* @__PURE__ */ new Set(["docs/context/amistio-project.md", "context/amistio-project.md"]);
5637
5654
  var generatedPathSegments = /* @__PURE__ */ new Set(["generated", "__generated__", "vendor", "vendors"]);
5655
+ var rootAgentGuidanceFiles = /* @__PURE__ */ new Set(["agents.md", "claude.md", "gemini.md", "codex.md"]);
5656
+ var extensionlessInstructionFiles = /* @__PURE__ */ new Set([".cursorrules", ".windsurfrules", ".clinerules"]);
5638
5657
  var documentFolderByType = {
5639
5658
  architecture: "docs/architecture",
5640
5659
  context: "docs/context",
@@ -5674,7 +5693,7 @@ async function scanLegacyDocuments(options) {
5674
5693
  skipped.push({ repoPath, reason: "excluded" });
5675
5694
  continue;
5676
5695
  }
5677
- const contentFormat = inferContentFormatFromRepoPath(repoPath);
5696
+ const contentFormat = importableContentFormatFromRepoPath(repoPath);
5678
5697
  if (!contentFormat) {
5679
5698
  skipped.push({ repoPath, reason: "notMarkdown" });
5680
5699
  continue;
@@ -5826,6 +5845,8 @@ function isAmistioManagedDocument(content) {
5826
5845
  return /^\s*<!--[\s\S]*?amistioDocumentId\s*:/i.test(content);
5827
5846
  }
5828
5847
  function classifyLegacyDocument(repoPath, content) {
5848
+ const localMemoryDocumentType = classifyLocalMemoryDocument(repoPath, content);
5849
+ if (localMemoryDocumentType) return localMemoryDocumentType;
5829
5850
  const haystack = `${repoPath} ${inferTitle2(content, repoPath)}`.toLowerCase();
5830
5851
  if (/\b(prompt|prompts|instruction|instructions|copilot|agent|skill)\b/.test(haystack)) return "prompt";
5831
5852
  if (/\b(adr|decision|decisions|rfc)\b/.test(haystack)) return "decision";
@@ -5837,10 +5858,10 @@ function classifyLegacyDocument(repoPath, content) {
5837
5858
  return "context";
5838
5859
  }
5839
5860
  function canonicalImportPath(sourcePath, documentType, contentFormat) {
5840
- if (isCanonicalControlPlanePath(sourcePath)) {
5861
+ if (isCanonicalControlPlanePath(sourcePath) && inferContentFormatFromRepoPath(sourcePath)) {
5841
5862
  return sourcePath;
5842
5863
  }
5843
- if (isLegacyControlPlanePath(sourcePath)) {
5864
+ if (isLegacyControlPlanePath(sourcePath) && inferContentFormatFromRepoPath(sourcePath)) {
5844
5865
  return `docs/${sourcePath}`;
5845
5866
  }
5846
5867
  const baseSlug = slugFromPath(sourcePath);
@@ -5859,6 +5880,60 @@ function uniqueDestinationPath(basePath, sourcePath, usedPaths) {
5859
5880
  usedPaths.add(uniquePath);
5860
5881
  return uniquePath;
5861
5882
  }
5883
+ function importableContentFormatFromRepoPath(repoPath) {
5884
+ return inferContentFormatFromRepoPath(repoPath) ?? localMemoryImportContentFormat(repoPath);
5885
+ }
5886
+ function localMemoryImportContentFormat(repoPath) {
5887
+ const normalized = normalizeRepoPath4(repoPath);
5888
+ const lowerPath = normalized.toLowerCase();
5889
+ const basename = lowerPath.split("/").at(-1) ?? lowerPath;
5890
+ if (extensionlessInstructionFiles.has(basename)) return "markdown";
5891
+ if (/\.mdc$/i.test(normalized) && isRepoLocalToolMemoryPath(normalized)) return "markdown";
5892
+ return void 0;
5893
+ }
5894
+ function classifyLocalMemoryDocument(repoPath, content) {
5895
+ const normalized = normalizeRepoPath4(repoPath);
5896
+ if (!isRepoLocalMemoryOrInstructionPath(normalized)) return void 0;
5897
+ if (isMemoryImportPath(normalized) || hasMemoryCue(normalized, content)) {
5898
+ return "memory";
5899
+ }
5900
+ if (isInstructionImportPath(normalized)) {
5901
+ return "prompt";
5902
+ }
5903
+ return void 0;
5904
+ }
5905
+ function isRepoLocalMemoryOrInstructionPath(repoPath) {
5906
+ return isMemoryImportPath(repoPath) || isInstructionImportPath(repoPath);
5907
+ }
5908
+ function isRepoLocalToolMemoryPath(repoPath) {
5909
+ return isRepoLocalMemoryOrInstructionPath(repoPath);
5910
+ }
5911
+ function isMemoryImportPath(repoPath) {
5912
+ const lowerPath = normalizeRepoPath4(repoPath).toLowerCase();
5913
+ return /^(docs\/memory|memory|memories|\.memory|\.memories)(\/|$)/.test(lowerPath);
5914
+ }
5915
+ function isInstructionImportPath(repoPath) {
5916
+ const normalized = normalizeRepoPath4(repoPath);
5917
+ const lowerPath = normalized.toLowerCase();
5918
+ const segments = lowerPath.split("/");
5919
+ const basename = segments.at(-1) ?? lowerPath;
5920
+ if (segments.length === 1 && rootAgentGuidanceFiles.has(basename)) return true;
5921
+ if (extensionlessInstructionFiles.has(basename)) return true;
5922
+ if (lowerPath === ".github/copilot-instructions.md") return true;
5923
+ if (lowerPath.startsWith(".github/instructions/") && lowerPath.endsWith(".instructions.md")) return true;
5924
+ if (lowerPath.startsWith(".cursor/rules/") && /\.(md|mdc)$/i.test(lowerPath)) return true;
5925
+ if (lowerPath.startsWith(".windsurf/rules/") && lowerPath.endsWith(".md")) return true;
5926
+ if (lowerPath.startsWith(".cline/") && lowerPath.endsWith(".md")) return true;
5927
+ if (lowerPath.startsWith(".roo/") && lowerPath.endsWith(".md")) return true;
5928
+ if (lowerPath.startsWith(".claude/") && lowerPath.endsWith(".md")) return true;
5929
+ if (lowerPath.startsWith(".gemini/") && lowerPath.endsWith(".md")) return true;
5930
+ if (lowerPath.startsWith(".vscode/") && lowerPath.endsWith(".instructions.md")) return true;
5931
+ return false;
5932
+ }
5933
+ function hasMemoryCue(repoPath, content) {
5934
+ const haystack = `${repoPath} ${inferTitle2(content, repoPath)}`.toLowerCase();
5935
+ return /\b(memory|memories|lesson|lessons|mistake|mistakes|learning|retro|retrospective|observation|observations)\b/.test(haystack);
5936
+ }
5862
5937
  function isCanonicalControlPlanePath(repoPath) {
5863
5938
  const [firstSegment, secondSegment, thirdSegment] = normalizeRepoPath4(repoPath).split("/");
5864
5939
  return firstSegment === "docs" && Boolean(secondSegment === "html" && thirdSegment && controlPlaneRoots2.includes(thirdSegment) || secondSegment && controlPlaneRoots2.includes(secondSegment));
@@ -5884,7 +5959,7 @@ function stripFrontmatter(content) {
5884
5959
  return closingLineEnd === -1 ? "" : content.slice(closingLineEnd + 1);
5885
5960
  }
5886
5961
  function slugFromPath(repoPath) {
5887
- const withoutExtension = repoPath.replace(/\.(md|mdx|html?)$/i, "");
5962
+ const withoutExtension = repoPath.replace(/\.(md|mdx|mdc|html?)$/i, "");
5888
5963
  const slug = withoutExtension.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 90);
5889
5964
  return slug || "imported-document";
5890
5965
  }