@prodero/curator 0.0.0-stage → 0.1.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/ARCHITECTURE.md +298 -0
- package/CHANGELOG.md +16 -0
- package/CONTRIBUTING.md +9 -0
- package/LICENSE +21 -0
- package/README.md +94 -2
- package/SECURITY.md +7 -0
- package/bin/prodero +7 -0
- package/dist/cli.js +228 -0
- package/dist/context-instructions.js +2 -0
- package/dist/contracts.js +50 -0
- package/dist/conversation-presentation.js +262 -0
- package/dist/core/agent-instructions.js +39 -0
- package/dist/core/applicability-label.js +11 -0
- package/dist/core/applicability.js +143 -0
- package/dist/core/bootstrap.js +129 -0
- package/dist/core/consolidation.js +315 -0
- package/dist/core/constants.js +21 -0
- package/dist/core/context-allocation.js +54 -0
- package/dist/core/context-snapshot.js +12 -0
- package/dist/core/context.js +489 -0
- package/dist/core/curation-clauses.js +68 -0
- package/dist/core/curation-evidence.js +98 -0
- package/dist/core/curation-graphical.js +105 -0
- package/dist/core/curation-review-presentation.js +4 -0
- package/dist/core/curation-review-state.js +62 -0
- package/dist/core/curation-review.js +573 -0
- package/dist/core/curation-revision.js +106 -0
- package/dist/core/curation-semantic-contract.js +239 -0
- package/dist/core/curation-semantic-jobs.js +280 -0
- package/dist/core/diagnostics.js +17 -0
- package/dist/core/format.js +114 -0
- package/dist/core/ids.js +6 -0
- package/dist/core/imports.js +169 -0
- package/dist/core/index.js +93 -0
- package/dist/core/instruction-review.js +86 -0
- package/dist/core/integrity-files.js +174 -0
- package/dist/core/intelligence.js +95 -0
- package/dist/core/knowledge-analysis.js +46 -0
- package/dist/core/knowledge-authority.js +143 -0
- package/dist/core/knowledge-curation.js +316 -0
- package/dist/core/knowledge-engine.js +475 -0
- package/dist/core/knowledge-graph.js +191 -0
- package/dist/core/knowledge-lifecycle.js +212 -0
- package/dist/core/knowledge-management.js +113 -0
- package/dist/core/knowledge-migration.js +84 -0
- package/dist/core/knowledge-policy.js +38 -0
- package/dist/core/knowledge-provenance.js +90 -0
- package/dist/core/knowledge-write-policy.js +85 -0
- package/dist/core/language.js +14 -0
- package/dist/core/matching.js +6 -0
- package/dist/core/memory-index.js +239 -0
- package/dist/core/memory-intelligence.js +288 -0
- package/dist/core/memory-link-repair.js +66 -0
- package/dist/core/memory-operations.js +122 -0
- package/dist/core/memory-pressure.js +212 -0
- package/dist/core/memory-sync-curator.js +62 -0
- package/dist/core/memory-sync.js +463 -0
- package/dist/core/memory-transformation-bundle.js +163 -0
- package/dist/core/memory-transformation-proposal.js +135 -0
- package/dist/core/memory-transformation.js +108 -0
- package/dist/core/memory.js +186 -0
- package/dist/core/migrations.js +569 -0
- package/dist/core/path-scope.js +27 -0
- package/dist/core/plugin-files.js +116 -0
- package/dist/core/project-root.js +39 -0
- package/dist/core/project.js +634 -0
- package/dist/core/promotion.js +279 -0
- package/dist/core/reconciliation.js +61 -0
- package/dist/core/secrets.js +67 -0
- package/dist/core/semantic-safety.js +33 -0
- package/dist/core/session-index.js +112 -0
- package/dist/core/session-review.js +150 -0
- package/dist/core/sessions.js +289 -0
- package/dist/core/settings.js +76 -0
- package/dist/core/skill-curation.js +239 -0
- package/dist/core/skill-polish.js +41 -0
- package/dist/core/skills.js +368 -0
- package/dist/core/storage.js +408 -0
- package/dist/core/sync-transaction.js +254 -0
- package/dist/core/telemetry.js +64 -0
- package/dist/core/templates.js +53 -0
- package/dist/core/types.js +2 -0
- package/dist/core/update.js +320 -0
- package/dist/curation-review-adapter.js +92 -0
- package/dist/diagnostic-boundary.js +54 -0
- package/dist/filesystem/permissions.js +39 -0
- package/dist/hosts/adapters.js +119 -0
- package/dist/hosts/files.js +133 -0
- package/dist/hosts/manager.js +150 -0
- package/dist/hosts/public-entry.js +47 -0
- package/dist/hosts/runtime-command.js +66 -0
- package/dist/hosts/runtime.js +139 -0
- package/dist/hosts/wizard.js +114 -0
- package/dist/knowledge-table.js +38 -0
- package/dist/mcp-development.js +69 -0
- package/dist/mcp-workspace.js +141 -0
- package/dist/memory-presentation.js +172 -0
- package/dist/plugins/api.js +2 -0
- package/dist/plugins/context.js +20 -0
- package/dist/plugins/mcp.js +72 -0
- package/dist/plugins/registry.js +106 -0
- package/dist/presentation-cards.js +32 -0
- package/dist/presentation-server.js +79 -0
- package/dist/presentation-trace.js +174 -0
- package/dist/presentation.js +412 -0
- package/dist/providers/mcp-sampling.js +45 -0
- package/dist/server.js +656 -0
- package/dist/settings-definition.js +22 -0
- package/dist/skill-instructions.js +2 -0
- package/dist-ui/index.html +214 -0
- package/docs/APPLICABILITY.md +151 -0
- package/docs/COMPATIBILITY.md +49 -0
- package/docs/CONTEXT-SELECTION-V2.md +61 -0
- package/docs/CURATION-APP-REVIEW.md +156 -0
- package/docs/CURATION-CLAUSE-REFINEMENT.md +45 -0
- package/docs/IMPORTER-CURATION.md +142 -0
- package/docs/KNOWLEDGE-AUTHORITY.md +165 -0
- package/docs/KNOWLEDGE-SCHEMA-V2.md +150 -0
- package/docs/MIGRATIONS.md +99 -0
- package/docs/PRIVACY.md +28 -0
- package/docs/RECOVERY.md +56 -0
- package/docs/RELEASING.md +40 -0
- package/docs/hosts.md +134 -0
- package/docs/plugins.md +83 -0
- package/docs/ui-themes.md +43 -0
- package/instructions/AGENTS.md +33 -0
- package/package.json +88 -4
- package/plugins/migrate_ai_studio/README.md +101 -0
- package/plugins/migrate_ai_studio/index.cjs +396 -0
- package/plugins/migrate_ai_studio/instructions/audit.md +25 -0
- package/plugins/migrate_ai_studio/instructions/classify.md +41 -0
- package/plugins/migrate_ai_studio/instructions/migrate.md +34 -0
- package/plugins/migrate_ai_studio/instructions/verify.md +20 -0
- package/plugins/migrate_ai_studio/plugin.json +11 -0
- package/plugins/migrate_ai_studio/schema.cjs +20 -0
- package/plugins/migrate_ai_studio/ui/dist/index.html +77 -0
- package/prodero.release.json +5 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { assertCompatibleProject } from "./storage.js";
|
|
2
|
+
import { hasSecret } from "./secrets.js";
|
|
3
|
+
export { hasSecret } from "./secrets.js";
|
|
4
|
+
import fs from "node:fs/promises";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import crypto from "node:crypto";
|
|
7
|
+
const skipped = new Set([".git", "node_modules", ".cache", ".transactions", "dist", "dist-ui"]);
|
|
8
|
+
export function safeRelative(value) {
|
|
9
|
+
if (!value || path.isAbsolute(value) || value.includes("\\") || /[\x00-\x1f]/.test(value) || value.split("/").some(p => !p || p === ".." || p === "."))
|
|
10
|
+
throw new Error("Unsafe relative path");
|
|
11
|
+
return value;
|
|
12
|
+
}
|
|
13
|
+
export async function checkedPath(root, relative) {
|
|
14
|
+
await assertCompatibleProject(root);
|
|
15
|
+
if (relative !== ".")
|
|
16
|
+
safeRelative(relative);
|
|
17
|
+
let cursor = await fs.realpath(root);
|
|
18
|
+
for (const part of relative === "." ? [] : relative.split("/")) {
|
|
19
|
+
cursor = path.join(cursor, part);
|
|
20
|
+
try {
|
|
21
|
+
if ((await fs.lstat(cursor)).isSymbolicLink())
|
|
22
|
+
throw new Error("Symlink source/destination rejected");
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
if (error.code !== "ENOENT")
|
|
26
|
+
throw error;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return cursor;
|
|
30
|
+
}
|
|
31
|
+
/** Conservative source exclusions: credential stores/config may expose more than one secret. */
|
|
32
|
+
export function secretPath(value) {
|
|
33
|
+
const normalized = value.replace(/\\/g, '/');
|
|
34
|
+
// Exclude these private configuration trees, including caches and nonstandard key names.
|
|
35
|
+
if (/(^|\/)(?:\.ssh|\.aws|\.azure)(?:\/|$)/i.test(normalized) || /(^|\/)(?:\.config\/gcloud|AppData\/Roaming\/gcloud)(?:\/|$)/i.test(normalized))
|
|
36
|
+
return true;
|
|
37
|
+
if (/(^|\/)gcloud\/(?:credentials\.db|access_tokens\.db|legacy_credentials(?:\/|$)|configurations(?:\/|$))/i.test(normalized))
|
|
38
|
+
return true;
|
|
39
|
+
if (/(^|\/)(?:ssh_config|sshd_config)(?:$|\.d(?:\/|$))/i.test(normalized))
|
|
40
|
+
return true;
|
|
41
|
+
if (/(^|\/)(?:\.kube\/config|\.docker\/config\.json)(?:\.[^/]*)?$/i.test(normalized))
|
|
42
|
+
return true;
|
|
43
|
+
const name = normalized.split('/').at(-1) ?? '';
|
|
44
|
+
if (/^(?:\.env(?:\..*)?|\.npmrc(?:\..*)?|\.git-credentials(?:\..*)?|\.netrc|_netrc|\.pypirc|\.boto|credentials(?:\..*)?|application_default_credentials\.json|id_(?:rsa|dsa|ecdsa|ed25519)(?:_sk)?|ssh_host_.*_key)$/i.test(name))
|
|
45
|
+
return true;
|
|
46
|
+
if (/\.(?:pem|key|p12|pfx|ppk)$/i.test(name))
|
|
47
|
+
return true;
|
|
48
|
+
// Credential-specific data filenames; do not blacklist ordinary auth modules or docs.
|
|
49
|
+
return /^(?:(?:[^/]+[-_.])?service[-_]?account(?:[-_]?(?:key|credentials))?(?:[-_.](?:prod|production|dev|development|staging|test))?|(?:[^/]+[-_.])?credentials|client[_-]?secret(?:[_-].*)?)\.(?:json|ya?ml|toml|ini)$/i.test(name);
|
|
50
|
+
}
|
|
51
|
+
export async function scanFiles(root, relative, limit = 2000) {
|
|
52
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > 20000)
|
|
53
|
+
throw new Error("Invalid file count limit");
|
|
54
|
+
const base = await checkedPath(root, relative);
|
|
55
|
+
const files = [];
|
|
56
|
+
let truncated = false, count = 0;
|
|
57
|
+
try {
|
|
58
|
+
if (!(await fs.stat(base)).isDirectory())
|
|
59
|
+
throw new Error("Source is not a directory");
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
if (error.code === "ENOENT")
|
|
63
|
+
return { files, truncated, exists: false };
|
|
64
|
+
throw error;
|
|
65
|
+
}
|
|
66
|
+
async function visit(dir, prefix) {
|
|
67
|
+
for (const entry of (await fs.readdir(dir, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
68
|
+
if ((relative === "." && (skipped.has(entry.name) || entry.name === ".prodero")) || (relative === ".prodero" && [".cache", ".transactions"].includes(entry.name)))
|
|
69
|
+
continue;
|
|
70
|
+
if (++count > limit) {
|
|
71
|
+
truncated = true;
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
if (entry.isSymbolicLink())
|
|
75
|
+
throw new Error("Symlink in source tree rejected");
|
|
76
|
+
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
77
|
+
if (entry.isDirectory())
|
|
78
|
+
await visit(path.join(dir, entry.name), rel);
|
|
79
|
+
else if (entry.isFile())
|
|
80
|
+
files.push({ path: rel, bytes: (await fs.stat(path.join(dir, entry.name))).size });
|
|
81
|
+
else
|
|
82
|
+
throw new Error("Special source file rejected");
|
|
83
|
+
if (truncated)
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
await visit(base, relative === "." ? "" : relative);
|
|
88
|
+
return { files, truncated, exists: true };
|
|
89
|
+
}
|
|
90
|
+
export async function readSource(root, relative) {
|
|
91
|
+
if (secretPath(relative))
|
|
92
|
+
throw new Error("Secret-bearing file type excluded");
|
|
93
|
+
const target = await checkedPath(root, relative);
|
|
94
|
+
const stat = await fs.stat(target);
|
|
95
|
+
if (!stat.isFile() || stat.size > 128000)
|
|
96
|
+
throw new Error("Source file exceeds text budget");
|
|
97
|
+
const raw = await fs.readFile(target);
|
|
98
|
+
if (raw.includes(0))
|
|
99
|
+
throw new Error("Binary source excluded");
|
|
100
|
+
const content = raw.toString("utf8");
|
|
101
|
+
if (hasSecret(content))
|
|
102
|
+
throw new Error("Potential secret-bearing source excluded");
|
|
103
|
+
return { path: relative, content, sha256: crypto.createHash("sha256").update(raw).digest("hex") };
|
|
104
|
+
}
|
|
105
|
+
/** External integrity scope: bounded, path-screened, Git-ignore aware; no source text retained. */
|
|
106
|
+
export async function snapshotFiles(root, relative, options = {}) {
|
|
107
|
+
return (await (await import("./integrity-files.js")).integritySnapshot(root, relative, options)).hashes;
|
|
108
|
+
}
|
|
109
|
+
/** Core-only exception: canonical Local records/checkpoints participate in import conflict/replay checks. */
|
|
110
|
+
export async function snapshotImportState(root) {
|
|
111
|
+
return (await (await import("./integrity-files.js")).integritySnapshot(root, ".prodero", {}, true)).hashes;
|
|
112
|
+
}
|
|
113
|
+
export async function digestTree(root, relative, options = {}) {
|
|
114
|
+
return (await (await import("./integrity-files.js")).integritySnapshot(root, relative, options)).digest;
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=plugin-files.js.map
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
/** Validate and retain the selected boundary; only CLI implicit discovery searches ancestors. */
|
|
4
|
+
export async function explicitProjectRoot(value) {
|
|
5
|
+
if (!value.trim() || !path.isAbsolute(value))
|
|
6
|
+
throw new Error("Project root must be an absolute existing directory.");
|
|
7
|
+
const root = path.resolve(value);
|
|
8
|
+
if (!(await fs.stat(root).catch(() => null))?.isDirectory())
|
|
9
|
+
throw new Error("Project root is not an existing directory. Select an absolute existing directory.");
|
|
10
|
+
// A new project inside a Git working tree would dirty that parent on init.
|
|
11
|
+
// Existing nested Prodero projects and standalone Git/worktree roots retain their boundary.
|
|
12
|
+
if (await fs.stat(path.join(root, ".git")).catch(() => null) || await fs.stat(path.join(root, ".prodero/config.json")).catch(() => null))
|
|
13
|
+
return root;
|
|
14
|
+
let parent = path.dirname(root);
|
|
15
|
+
while (true) {
|
|
16
|
+
if (await fs.stat(path.join(parent, ".git")).catch(() => null)) {
|
|
17
|
+
throw new Error("PRODERO_NESTED_ROOT: The selected directory is inside another Git repository and is not an initialized Prodero project. Select the intended repository root explicitly or create a standalone Git repository in the selected directory. The parent was not selected or changed.");
|
|
18
|
+
}
|
|
19
|
+
const next = path.dirname(parent);
|
|
20
|
+
if (next === parent)
|
|
21
|
+
break;
|
|
22
|
+
parent = next;
|
|
23
|
+
}
|
|
24
|
+
return root;
|
|
25
|
+
}
|
|
26
|
+
export async function cliProjectRoot(value) {
|
|
27
|
+
if (value !== undefined)
|
|
28
|
+
return explicitProjectRoot(value);
|
|
29
|
+
let current = process.cwd();
|
|
30
|
+
while (true) {
|
|
31
|
+
if (await fs.stat(path.join(current, ".git")).catch(() => null) || await fs.stat(path.join(current, ".prodero/config.json")).catch(() => null))
|
|
32
|
+
return current;
|
|
33
|
+
const parent = path.dirname(current);
|
|
34
|
+
if (parent === current)
|
|
35
|
+
throw new Error("No project found. Use --root /absolute/project to select an existing directory explicitly.");
|
|
36
|
+
current = parent;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=project-root.js.map
|