@noir-ai/core 1.0.0-beta.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.
- package/LICENSE +21 -0
- package/README.md +23 -0
- package/dist/index.d.ts +112 -0
- package/dist/index.js +201 -0
- package/dist/index.js.map +1 -0
- package/package.json +56 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 agaaaptr
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# @noir-ai/core
|
|
2
|
+
|
|
3
|
+
Shared domain types, the configuration schema (`NoirConfigSchema`), the `.noir/` on-disk layout, and the marker constants that the rest of the Noir toolkit is built on. This is the type-contract package — it holds no runtime I/O of its own.
|
|
4
|
+
|
|
5
|
+
Part of the **[Noir](https://github.com/agaaaptr/noir#readme)** toolkit — the discipline, context, and memory layer for any agentic CLI.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @noir-ai/core
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
> Most users install the CLI instead, which pulls in the packages it needs:
|
|
14
|
+
>
|
|
15
|
+
> ```bash
|
|
16
|
+
> npm install -g @noir-ai/cli
|
|
17
|
+
> ```
|
|
18
|
+
|
|
19
|
+
This package is mainly consumed by the other `@noir-ai/*` packages. For programmatic usage, see the [root README](https://github.com/agaaaptr/noir#readme) and [`docs/usage.md`](https://github.com/agaaaptr/noir/blob/main/docs/usage.md).
|
|
20
|
+
|
|
21
|
+
## License
|
|
22
|
+
|
|
23
|
+
MIT © agaaaptr
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import * as z from 'zod';
|
|
2
|
+
|
|
3
|
+
declare const NoirConfigSchema: z.ZodObject<{
|
|
4
|
+
host: z.ZodLiteral<"claude">;
|
|
5
|
+
name: z.ZodOptional<z.ZodString>;
|
|
6
|
+
mode: z.ZodDefault<z.ZodEnum<{
|
|
7
|
+
full: "full";
|
|
8
|
+
quick: "quick";
|
|
9
|
+
}>>;
|
|
10
|
+
daemon: z.ZodDefault<z.ZodObject<{
|
|
11
|
+
idleTimeoutSec: z.ZodDefault<z.ZodNumber>;
|
|
12
|
+
port: z.ZodOptional<z.ZodNumber>;
|
|
13
|
+
}, z.core.$strip>>;
|
|
14
|
+
context: z.ZodDefault<z.ZodObject<{
|
|
15
|
+
embedder: z.ZodDefault<z.ZodObject<{
|
|
16
|
+
kind: z.ZodDefault<z.ZodEnum<{
|
|
17
|
+
local: "local";
|
|
18
|
+
remote: "remote";
|
|
19
|
+
ollama: "ollama";
|
|
20
|
+
none: "none";
|
|
21
|
+
}>>;
|
|
22
|
+
model: z.ZodOptional<z.ZodString>;
|
|
23
|
+
provider: z.ZodOptional<z.ZodString>;
|
|
24
|
+
baseURL: z.ZodOptional<z.ZodString>;
|
|
25
|
+
dim: z.ZodDefault<z.ZodNumber>;
|
|
26
|
+
}, z.core.$strip>>;
|
|
27
|
+
roots: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
28
|
+
budgetTokens: z.ZodDefault<z.ZodNumber>;
|
|
29
|
+
}, z.core.$strip>>;
|
|
30
|
+
model: z.ZodDefault<z.ZodObject<{
|
|
31
|
+
defaultProvider: z.ZodOptional<z.ZodString>;
|
|
32
|
+
tiers: z.ZodOptional<z.ZodObject<{
|
|
33
|
+
draft: z.ZodOptional<z.ZodString>;
|
|
34
|
+
title: z.ZodOptional<z.ZodString>;
|
|
35
|
+
summarize: z.ZodOptional<z.ZodString>;
|
|
36
|
+
consolidate: z.ZodOptional<z.ZodString>;
|
|
37
|
+
}, z.core.$strip>>;
|
|
38
|
+
providers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
39
|
+
model: z.ZodString;
|
|
40
|
+
baseURL: z.ZodOptional<z.ZodString>;
|
|
41
|
+
apiKeyEnv: z.ZodOptional<z.ZodString>;
|
|
42
|
+
}, z.core.$strip>>>;
|
|
43
|
+
}, z.core.$strip>>;
|
|
44
|
+
memory: z.ZodDefault<z.ZodObject<{
|
|
45
|
+
consolidation: z.ZodDefault<z.ZodObject<{
|
|
46
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
47
|
+
provider: z.ZodOptional<z.ZodString>;
|
|
48
|
+
model: z.ZodOptional<z.ZodString>;
|
|
49
|
+
types: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
50
|
+
}, z.core.$strip>>;
|
|
51
|
+
}, z.core.$strip>>;
|
|
52
|
+
}, z.core.$strip>;
|
|
53
|
+
type NoirConfig = z.infer<typeof NoirConfigSchema>;
|
|
54
|
+
declare function parseConfig(raw: unknown): NoirConfig;
|
|
55
|
+
|
|
56
|
+
declare const NOIR_DIR = ".noir";
|
|
57
|
+
/**
|
|
58
|
+
* User-global Noir home: `~/.noir` (HOME-relative, NOT project `.noir/`).
|
|
59
|
+
*
|
|
60
|
+
* Hosts cross-project, user-scoped state — today the daemon record
|
|
61
|
+
* (`~/.noir/daemon.json`, written by @noir-ai/daemon) and the embedding-model
|
|
62
|
+
* cache ({@link modelsDir}). Kept HOME-relative so a single project checkout
|
|
63
|
+
* stays portable across machines (blueprint: `ProjectId` is canonical, never a
|
|
64
|
+
* filesystem path; the project `.noir/` dir never holds machine-local caches).
|
|
65
|
+
*/
|
|
66
|
+
declare function noirHome(): string;
|
|
67
|
+
/**
|
|
68
|
+
* User-global cache for downloaded embedding-model weights: `~/.noir/models/`.
|
|
69
|
+
*
|
|
70
|
+
* Used by @noir-ai/context's local embedder (`@huggingface/transformers` pins
|
|
71
|
+
* `env.cacheDir` here on first load). HOME-relative per spec OQ-7 (resolved) —
|
|
72
|
+
* the ~22 MB MiniLM download is user-scoped, not per-project, so projects stay
|
|
73
|
+
* portable and the weight is fetched at most once per machine.
|
|
74
|
+
*/
|
|
75
|
+
declare function modelsDir(): string;
|
|
76
|
+
declare const paths: {
|
|
77
|
+
readonly noirDir: (root: string) => string;
|
|
78
|
+
readonly noirMd: (root: string) => string;
|
|
79
|
+
readonly config: (root: string) => string;
|
|
80
|
+
readonly projectId: (root: string) => string;
|
|
81
|
+
readonly storeDir: (root: string) => string;
|
|
82
|
+
readonly storeDb: (root: string, projectId: string) => string;
|
|
83
|
+
readonly specsDir: (root: string) => string;
|
|
84
|
+
readonly specFile: (root: string, taskId: string, slug: string) => string;
|
|
85
|
+
readonly plansDir: (root: string) => string;
|
|
86
|
+
readonly planFile: (root: string, taskId: string, slug: string) => string;
|
|
87
|
+
readonly tasksDir: (root: string) => string;
|
|
88
|
+
readonly taskFile: (root: string, taskId: string, taskName: string) => string;
|
|
89
|
+
readonly decisionsDir: (root: string) => string;
|
|
90
|
+
readonly decisionFile: (root: string, n: number) => string;
|
|
91
|
+
readonly auditDir: (root: string) => string;
|
|
92
|
+
readonly auditFile: (root: string, taskId: string) => string;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
declare const CONTEXT_BLOCK_BEGIN = "<!-- noir:context begin -->";
|
|
96
|
+
declare const CONTEXT_BLOCK_END = "<!-- noir:context end -->";
|
|
97
|
+
|
|
98
|
+
/** Canonical, machine-stable project identity. NEVER a filesystem path. */
|
|
99
|
+
type ProjectId = string;
|
|
100
|
+
declare function createProjectId(): ProjectId;
|
|
101
|
+
|
|
102
|
+
interface ProjectInfo {
|
|
103
|
+
id: ProjectId;
|
|
104
|
+
name: string;
|
|
105
|
+
root: string;
|
|
106
|
+
config: NoirConfig;
|
|
107
|
+
}
|
|
108
|
+
declare function loadProjectInfo(root: string): ProjectInfo;
|
|
109
|
+
|
|
110
|
+
declare const NOIR_VERSION: string;
|
|
111
|
+
|
|
112
|
+
export { CONTEXT_BLOCK_BEGIN, CONTEXT_BLOCK_END, NOIR_DIR, NOIR_VERSION, type NoirConfig, NoirConfigSchema, type ProjectId, type ProjectInfo, createProjectId, loadProjectInfo, modelsDir, noirHome, parseConfig, paths };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
// src/config.ts
|
|
2
|
+
import * as z from "zod";
|
|
3
|
+
var NoirConfigSchema = z.object({
|
|
4
|
+
host: z.literal("claude"),
|
|
5
|
+
name: z.string().optional(),
|
|
6
|
+
mode: z.enum(["full", "quick"]).default("full"),
|
|
7
|
+
daemon: z.object({
|
|
8
|
+
idleTimeoutSec: z.number().int().positive().default(900),
|
|
9
|
+
port: z.number().int().min(0).max(65535).optional()
|
|
10
|
+
}).default({ idleTimeoutSec: 900 }),
|
|
11
|
+
// Slice S6 context layer (@noir-ai/context). Mirrors the `daemon` idiom — a
|
|
12
|
+
// top-level object with `.default({})` so a config with NO `context:` block
|
|
13
|
+
// still parses and behaves as local-embedder-attempted (AC-7 / NFR-6). The
|
|
14
|
+
// embedder shape is `kind`-based to match the discriminated `EmbedderConfig`
|
|
15
|
+
// the context factory consumes; `resolveEmbedderConfig` (@noir-ai/context) is
|
|
16
|
+
// the single bridge from this user-facing schema to the factory input, so core
|
|
17
|
+
// never imports context (no core→context cycle). Provider-explicit, NEVER
|
|
18
|
+
// silent remote (blueprint D6): `kind:'remote'`/`'ollama'` are opt-in only;
|
|
19
|
+
// the default `'local'` is in-process, offline, free, private.
|
|
20
|
+
context: z.object({
|
|
21
|
+
embedder: z.object({
|
|
22
|
+
kind: z.enum(["local", "remote", "ollama", "none"]).default("local"),
|
|
23
|
+
// HF repo id (local) / provider model id (remote) / Ollama tag.
|
|
24
|
+
model: z.string().optional(),
|
|
25
|
+
// Remote provider key, e.g. 'openai' (only meaningful when kind:'remote').
|
|
26
|
+
// Free-form string so openai-compatible providers stay expressible; the
|
|
27
|
+
// resolver maps known names (openai/voyage/cohere) to their API-key env var.
|
|
28
|
+
provider: z.string().optional(),
|
|
29
|
+
// Ollama base URL, e.g. http://localhost:11434 (only when kind:'ollama').
|
|
30
|
+
baseURL: z.string().optional(),
|
|
31
|
+
// Target dimensionality — must be 384 to match the existing vec0 table.
|
|
32
|
+
dim: z.number().int().positive().default(384)
|
|
33
|
+
}).default({ kind: "local", dim: 384 }),
|
|
34
|
+
// Configured index roots (informational in core; the daemon/indexer consume).
|
|
35
|
+
roots: z.array(z.string()).default([]),
|
|
36
|
+
// Default token budget for a `context_search` result set (retriever default).
|
|
37
|
+
budgetTokens: z.number().int().positive().default(4096)
|
|
38
|
+
}).default({ embedder: { kind: "local", dim: 384 }, roots: [], budgetTokens: 4096 }),
|
|
39
|
+
// Slice S8 bounded model layer (@noir-ai/model). Mirrors the `daemon` idiom —
|
|
40
|
+
// a top-level object with `.default({})` so a config with NO `model:` block
|
|
41
|
+
// still parses and behaves as fully-degraded (every `complete()` call returns
|
|
42
|
+
// `null`; callers substitute a template — the always-available offline path,
|
|
43
|
+
// blueprint D5 / DS-5). `resolveModelConfig` (@noir-ai/model) is the single
|
|
44
|
+
// bridge from this user-facing schema to the runtime shape `complete()`
|
|
45
|
+
// consumes, so core never imports model (no core→model cycle).
|
|
46
|
+
//
|
|
47
|
+
// Provider-EXPLICIT, never silent paid (blueprint D5 / DS-6): the provider is
|
|
48
|
+
// resolved ONLY from explicit `defaultProvider` / a tier's provider key. Env-
|
|
49
|
+
// var presence is NEVER consulted to pick a provider — `ANTHROPIC_API_KEY`
|
|
50
|
+
// being set for another tool does NOT activate Anthropic in Noir. No explicit,
|
|
51
|
+
// configured provider ⇒ `null`. Secrets live in env vars only (DS-8):
|
|
52
|
+
// `apiKeyEnv` stores the env-var NAME (`ANTHROPIC_API_KEY`), never the value,
|
|
53
|
+
// so `.noir/config.yml` stays safe to commit and share; `complete()` reads the
|
|
54
|
+
// value at call time. `tiers` map a tier name → provider block key; each
|
|
55
|
+
// `providers[name]` declares the model id, optional `baseURL` (openai-compat),
|
|
56
|
+
// and optional `apiKeyEnv` (omit for anonymous local providers like Ollama).
|
|
57
|
+
model: z.object({
|
|
58
|
+
// Fallback provider name (key into `providers`) when a call's tier resolves
|
|
59
|
+
// no explicit provider. Free-form string so openai-compatible providers stay
|
|
60
|
+
// expressible without an enum churn.
|
|
61
|
+
defaultProvider: z.string().optional(),
|
|
62
|
+
// Per-tier provider-key overrides (draft / title / summarize / consolidate).
|
|
63
|
+
// Each value is a key into `providers{}`; resolution is the consumer's job
|
|
64
|
+
// (tier → tier.provider → defaultProvider → providers[name]).
|
|
65
|
+
tiers: z.object({
|
|
66
|
+
draft: z.string().optional(),
|
|
67
|
+
title: z.string().optional(),
|
|
68
|
+
summarize: z.string().optional(),
|
|
69
|
+
consolidate: z.string().optional()
|
|
70
|
+
}).optional(),
|
|
71
|
+
// Configured provider blocks, keyed by name. `model` is required (a provider
|
|
72
|
+
// without a model id is meaningless); `apiKeyEnv` is omitted for anonymous
|
|
73
|
+
// local providers (Ollama / LM Studio) which then send no auth header.
|
|
74
|
+
providers: z.record(
|
|
75
|
+
z.string(),
|
|
76
|
+
z.object({
|
|
77
|
+
model: z.string(),
|
|
78
|
+
baseURL: z.string().optional(),
|
|
79
|
+
apiKeyEnv: z.string().optional()
|
|
80
|
+
})
|
|
81
|
+
).optional()
|
|
82
|
+
}).default({}),
|
|
83
|
+
// Slice S7 cross-session memory (@noir-ai/memory). Mirrors the `daemon` idiom —
|
|
84
|
+
// a top-level object with `.default({})` so a config with NO `memory:` block
|
|
85
|
+
// still parses and behaves as consolidation-disabled (the safe default —
|
|
86
|
+
// capture/store/retrieve are always local + free; consolidation is the ONLY
|
|
87
|
+
// LLM touch and it is opt-in + provider-explicit). `resolveMemoryConfig`
|
|
88
|
+
// (@noir-ai/memory) is the single bridge from this user-facing schema to the
|
|
89
|
+
// runtime `MemoryConfig` the engine consumes, so core never imports memory
|
|
90
|
+
// (no core→memory cycle; mirrors @noir-ai/context + @noir-ai/model).
|
|
91
|
+
//
|
|
92
|
+
// Provider-EXPLICIT, never silent paid (blueprint D6 / DS-6): the provider is
|
|
93
|
+
// resolved ONLY from explicit `consolidation.provider`. Env-var presence is
|
|
94
|
+
// NEVER consulted to pick a provider — `ANTHROPIC_API_KEY` being set for
|
|
95
|
+
// another tool does NOT activate consolidation. No explicit, enabled provider
|
|
96
|
+
// ⇒ `runConsolidation` refuses with `'no-provider'` + writes a miss audit, and
|
|
97
|
+
// NO S8 `complete()` call is made (the Agent-Memory anti-pattern, §9). The
|
|
98
|
+
// outer default matches the parsed output shape (Zod v4 requirement).
|
|
99
|
+
memory: z.object({
|
|
100
|
+
consolidation: z.object({
|
|
101
|
+
// Master switch (default false). When false, `consolidate` refuses +
|
|
102
|
+
// logs (`no-provider`) regardless of provider/model — the first gate.
|
|
103
|
+
enabled: z.boolean().default(false),
|
|
104
|
+
// Provider key, e.g. 'anthropic' | 'openai' | 'ollama'. Free-form
|
|
105
|
+
// string so openai-compatible providers stay expressible without an
|
|
106
|
+
// enum churn; required (alongside enabled) for consolidation to run.
|
|
107
|
+
provider: z.string().optional(),
|
|
108
|
+
// Provider-specific model id (consumed by S8 complete(); optional
|
|
109
|
+
// here only because a future anonymous local provider may not need it
|
|
110
|
+
// — runConsolidation still refuses when model is absent).
|
|
111
|
+
model: z.string().optional(),
|
|
112
|
+
// Restrict candidates to these types (default: every non-`lesson`).
|
|
113
|
+
types: z.array(z.string()).optional()
|
|
114
|
+
}).default({ enabled: false })
|
|
115
|
+
}).default({ consolidation: { enabled: false } })
|
|
116
|
+
});
|
|
117
|
+
function parseConfig(raw) {
|
|
118
|
+
return NoirConfigSchema.parse(raw);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// src/layout.ts
|
|
122
|
+
import { homedir } from "os";
|
|
123
|
+
import { join } from "path";
|
|
124
|
+
var NOIR_DIR = ".noir";
|
|
125
|
+
function noirHome() {
|
|
126
|
+
return join(homedir(), NOIR_DIR);
|
|
127
|
+
}
|
|
128
|
+
function modelsDir() {
|
|
129
|
+
return join(noirHome(), "models");
|
|
130
|
+
}
|
|
131
|
+
var paths = {
|
|
132
|
+
noirDir: (root) => join(root, NOIR_DIR),
|
|
133
|
+
noirMd: (root) => join(root, NOIR_DIR, "NOIR.md"),
|
|
134
|
+
config: (root) => join(root, NOIR_DIR, "config.yml"),
|
|
135
|
+
projectId: (root) => join(root, NOIR_DIR, "project.id"),
|
|
136
|
+
storeDir: (root) => join(root, NOIR_DIR, "store"),
|
|
137
|
+
storeDb: (root, projectId) => join(root, NOIR_DIR, "store", `${projectId}.db`),
|
|
138
|
+
// Artifact directories and files
|
|
139
|
+
specsDir: (root) => join(root, NOIR_DIR, "specs"),
|
|
140
|
+
specFile: (root, taskId, slug) => join(root, NOIR_DIR, "specs", `${taskId}-${slug}.md`),
|
|
141
|
+
plansDir: (root) => join(root, NOIR_DIR, "plans"),
|
|
142
|
+
planFile: (root, taskId, slug) => join(root, NOIR_DIR, "plans", `${taskId}-${slug}.md`),
|
|
143
|
+
tasksDir: (root) => join(root, NOIR_DIR, "tasks"),
|
|
144
|
+
taskFile: (root, taskId, taskName) => join(root, NOIR_DIR, "tasks", `${taskId}-${taskName}.md`),
|
|
145
|
+
decisionsDir: (root) => join(root, NOIR_DIR, "decisions"),
|
|
146
|
+
decisionFile: (root, n) => join(root, NOIR_DIR, "decisions", `${String(n).padStart(4, "0")}.md`),
|
|
147
|
+
auditDir: (root) => join(root, NOIR_DIR, "audit"),
|
|
148
|
+
auditFile: (root, taskId) => join(root, NOIR_DIR, "audit", `${taskId}.json`)
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
// src/markers.ts
|
|
152
|
+
var CONTEXT_BLOCK_BEGIN = "<!-- noir:context begin -->";
|
|
153
|
+
var CONTEXT_BLOCK_END = "<!-- noir:context end -->";
|
|
154
|
+
|
|
155
|
+
// src/project.ts
|
|
156
|
+
import { readFileSync } from "fs";
|
|
157
|
+
import { basename } from "path";
|
|
158
|
+
import { parse as parseYaml } from "yaml";
|
|
159
|
+
function loadProjectInfo(root) {
|
|
160
|
+
let rawId;
|
|
161
|
+
let rawConfig;
|
|
162
|
+
try {
|
|
163
|
+
rawId = readFileSync(paths.projectId(root), "utf8").trim();
|
|
164
|
+
rawConfig = parseYaml(readFileSync(paths.config(root), "utf8"));
|
|
165
|
+
} catch {
|
|
166
|
+
throw new Error(`Noir is not initialized in ${root}. Run \`noir init\` first.`);
|
|
167
|
+
}
|
|
168
|
+
const config = parseConfig(rawConfig);
|
|
169
|
+
return {
|
|
170
|
+
id: rawId,
|
|
171
|
+
name: config.name ?? basename(root),
|
|
172
|
+
root,
|
|
173
|
+
config
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// src/project-id.ts
|
|
178
|
+
import { randomUUID } from "crypto";
|
|
179
|
+
function createProjectId() {
|
|
180
|
+
return randomUUID();
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// src/version.ts
|
|
184
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
185
|
+
import { fileURLToPath } from "url";
|
|
186
|
+
var pkgPath = fileURLToPath(new URL("../package.json", import.meta.url));
|
|
187
|
+
var NOIR_VERSION = JSON.parse(readFileSync2(pkgPath, "utf8")).version ?? "0.0.0";
|
|
188
|
+
export {
|
|
189
|
+
CONTEXT_BLOCK_BEGIN,
|
|
190
|
+
CONTEXT_BLOCK_END,
|
|
191
|
+
NOIR_DIR,
|
|
192
|
+
NOIR_VERSION,
|
|
193
|
+
NoirConfigSchema,
|
|
194
|
+
createProjectId,
|
|
195
|
+
loadProjectInfo,
|
|
196
|
+
modelsDir,
|
|
197
|
+
noirHome,
|
|
198
|
+
parseConfig,
|
|
199
|
+
paths
|
|
200
|
+
};
|
|
201
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/config.ts","../src/layout.ts","../src/markers.ts","../src/project.ts","../src/project-id.ts","../src/version.ts"],"sourcesContent":["import * as z from 'zod';\n\nexport const NoirConfigSchema = z.object({\n host: z.literal('claude'),\n name: z.string().optional(),\n mode: z.enum(['full', 'quick']).default('full'),\n daemon: z\n .object({\n idleTimeoutSec: z.number().int().positive().default(900),\n port: z.number().int().min(0).max(65535).optional(),\n })\n .default({ idleTimeoutSec: 900 }),\n // Slice S6 context layer (@noir-ai/context). Mirrors the `daemon` idiom — a\n // top-level object with `.default({})` so a config with NO `context:` block\n // still parses and behaves as local-embedder-attempted (AC-7 / NFR-6). The\n // embedder shape is `kind`-based to match the discriminated `EmbedderConfig`\n // the context factory consumes; `resolveEmbedderConfig` (@noir-ai/context) is\n // the single bridge from this user-facing schema to the factory input, so core\n // never imports context (no core→context cycle). Provider-explicit, NEVER\n // silent remote (blueprint D6): `kind:'remote'`/`'ollama'` are opt-in only;\n // the default `'local'` is in-process, offline, free, private.\n context: z\n .object({\n embedder: z\n .object({\n kind: z.enum(['local', 'remote', 'ollama', 'none']).default('local'),\n // HF repo id (local) / provider model id (remote) / Ollama tag.\n model: z.string().optional(),\n // Remote provider key, e.g. 'openai' (only meaningful when kind:'remote').\n // Free-form string so openai-compatible providers stay expressible; the\n // resolver maps known names (openai/voyage/cohere) to their API-key env var.\n provider: z.string().optional(),\n // Ollama base URL, e.g. http://localhost:11434 (only when kind:'ollama').\n baseURL: z.string().optional(),\n // Target dimensionality — must be 384 to match the existing vec0 table.\n dim: z.number().int().positive().default(384),\n })\n .default({ kind: 'local', dim: 384 }),\n // Configured index roots (informational in core; the daemon/indexer consume).\n roots: z.array(z.string()).default([]),\n // Default token budget for a `context_search` result set (retriever default).\n budgetTokens: z.number().int().positive().default(4096),\n })\n // Zod v4 requires the outer default to match the parsed output shape (every\n // inner field already carries its own default, so an absent `context:` block\n // still resolves to local-embedder/empty-roots/4096). Mirrors `daemon:`.\n .default({ embedder: { kind: 'local', dim: 384 }, roots: [], budgetTokens: 4096 }),\n // Slice S8 bounded model layer (@noir-ai/model). Mirrors the `daemon` idiom —\n // a top-level object with `.default({})` so a config with NO `model:` block\n // still parses and behaves as fully-degraded (every `complete()` call returns\n // `null`; callers substitute a template — the always-available offline path,\n // blueprint D5 / DS-5). `resolveModelConfig` (@noir-ai/model) is the single\n // bridge from this user-facing schema to the runtime shape `complete()`\n // consumes, so core never imports model (no core→model cycle).\n //\n // Provider-EXPLICIT, never silent paid (blueprint D5 / DS-6): the provider is\n // resolved ONLY from explicit `defaultProvider` / a tier's provider key. Env-\n // var presence is NEVER consulted to pick a provider — `ANTHROPIC_API_KEY`\n // being set for another tool does NOT activate Anthropic in Noir. No explicit,\n // configured provider ⇒ `null`. Secrets live in env vars only (DS-8):\n // `apiKeyEnv` stores the env-var NAME (`ANTHROPIC_API_KEY`), never the value,\n // so `.noir/config.yml` stays safe to commit and share; `complete()` reads the\n // value at call time. `tiers` map a tier name → provider block key; each\n // `providers[name]` declares the model id, optional `baseURL` (openai-compat),\n // and optional `apiKeyEnv` (omit for anonymous local providers like Ollama).\n model: z\n .object({\n // Fallback provider name (key into `providers`) when a call's tier resolves\n // no explicit provider. Free-form string so openai-compatible providers stay\n // expressible without an enum churn.\n defaultProvider: z.string().optional(),\n // Per-tier provider-key overrides (draft / title / summarize / consolidate).\n // Each value is a key into `providers{}`; resolution is the consumer's job\n // (tier → tier.provider → defaultProvider → providers[name]).\n tiers: z\n .object({\n draft: z.string().optional(),\n title: z.string().optional(),\n summarize: z.string().optional(),\n consolidate: z.string().optional(),\n })\n .optional(),\n // Configured provider blocks, keyed by name. `model` is required (a provider\n // without a model id is meaningless); `apiKeyEnv` is omitted for anonymous\n // local providers (Ollama / LM Studio) which then send no auth header.\n providers: z\n .record(\n z.string(),\n z.object({\n model: z.string(),\n baseURL: z.string().optional(),\n apiKeyEnv: z.string().optional(),\n }),\n )\n .optional(),\n })\n // Absent `model:` block ⇒ `{}` ⇒ every tier/provider is undefined ⇒ full\n // degradation (offline, free, the default). Inner fields are `.optional()`\n // (no inner `.default()`) so a present-but-empty block also degrades.\n .default({}),\n // Slice S7 cross-session memory (@noir-ai/memory). Mirrors the `daemon` idiom —\n // a top-level object with `.default({})` so a config with NO `memory:` block\n // still parses and behaves as consolidation-disabled (the safe default —\n // capture/store/retrieve are always local + free; consolidation is the ONLY\n // LLM touch and it is opt-in + provider-explicit). `resolveMemoryConfig`\n // (@noir-ai/memory) is the single bridge from this user-facing schema to the\n // runtime `MemoryConfig` the engine consumes, so core never imports memory\n // (no core→memory cycle; mirrors @noir-ai/context + @noir-ai/model).\n //\n // Provider-EXPLICIT, never silent paid (blueprint D6 / DS-6): the provider is\n // resolved ONLY from explicit `consolidation.provider`. Env-var presence is\n // NEVER consulted to pick a provider — `ANTHROPIC_API_KEY` being set for\n // another tool does NOT activate consolidation. No explicit, enabled provider\n // ⇒ `runConsolidation` refuses with `'no-provider'` + writes a miss audit, and\n // NO S8 `complete()` call is made (the Agent-Memory anti-pattern, §9). The\n // outer default matches the parsed output shape (Zod v4 requirement).\n memory: z\n .object({\n consolidation: z\n .object({\n // Master switch (default false). When false, `consolidate` refuses +\n // logs (`no-provider`) regardless of provider/model — the first gate.\n enabled: z.boolean().default(false),\n // Provider key, e.g. 'anthropic' | 'openai' | 'ollama'. Free-form\n // string so openai-compatible providers stay expressible without an\n // enum churn; required (alongside enabled) for consolidation to run.\n provider: z.string().optional(),\n // Provider-specific model id (consumed by S8 complete(); optional\n // here only because a future anonymous local provider may not need it\n // — runConsolidation still refuses when model is absent).\n model: z.string().optional(),\n // Restrict candidates to these types (default: every non-`lesson`).\n types: z.array(z.string()).optional(),\n })\n // Absent consolidation block ⇒ disabled. Outer shape matches output.\n .default({ enabled: false }),\n })\n .default({ consolidation: { enabled: false } }),\n});\n\nexport type NoirConfig = z.infer<typeof NoirConfigSchema>;\n\nexport function parseConfig(raw: unknown): NoirConfig {\n return NoirConfigSchema.parse(raw);\n}\n","import { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nexport const NOIR_DIR = '.noir';\n\n/**\n * User-global Noir home: `~/.noir` (HOME-relative, NOT project `.noir/`).\n *\n * Hosts cross-project, user-scoped state — today the daemon record\n * (`~/.noir/daemon.json`, written by @noir-ai/daemon) and the embedding-model\n * cache ({@link modelsDir}). Kept HOME-relative so a single project checkout\n * stays portable across machines (blueprint: `ProjectId` is canonical, never a\n * filesystem path; the project `.noir/` dir never holds machine-local caches).\n */\nexport function noirHome(): string {\n return join(homedir(), NOIR_DIR);\n}\n\n/**\n * User-global cache for downloaded embedding-model weights: `~/.noir/models/`.\n *\n * Used by @noir-ai/context's local embedder (`@huggingface/transformers` pins\n * `env.cacheDir` here on first load). HOME-relative per spec OQ-7 (resolved) —\n * the ~22 MB MiniLM download is user-scoped, not per-project, so projects stay\n * portable and the weight is fetched at most once per machine.\n */\nexport function modelsDir(): string {\n return join(noirHome(), 'models');\n}\n\nexport const paths = {\n noirDir: (root: string) => join(root, NOIR_DIR),\n noirMd: (root: string) => join(root, NOIR_DIR, 'NOIR.md'),\n config: (root: string) => join(root, NOIR_DIR, 'config.yml'),\n projectId: (root: string) => join(root, NOIR_DIR, 'project.id'),\n storeDir: (root: string) => join(root, NOIR_DIR, 'store'),\n storeDb: (root: string, projectId: string) => join(root, NOIR_DIR, 'store', `${projectId}.db`),\n // Artifact directories and files\n specsDir: (root: string) => join(root, NOIR_DIR, 'specs'),\n specFile: (root: string, taskId: string, slug: string) =>\n join(root, NOIR_DIR, 'specs', `${taskId}-${slug}.md`),\n plansDir: (root: string) => join(root, NOIR_DIR, 'plans'),\n planFile: (root: string, taskId: string, slug: string) =>\n join(root, NOIR_DIR, 'plans', `${taskId}-${slug}.md`),\n tasksDir: (root: string) => join(root, NOIR_DIR, 'tasks'),\n taskFile: (root: string, taskId: string, taskName: string) =>\n join(root, NOIR_DIR, 'tasks', `${taskId}-${taskName}.md`),\n decisionsDir: (root: string) => join(root, NOIR_DIR, 'decisions'),\n decisionFile: (root: string, n: number) =>\n join(root, NOIR_DIR, 'decisions', `${String(n).padStart(4, '0')}.md`),\n auditDir: (root: string) => join(root, NOIR_DIR, 'audit'),\n auditFile: (root: string, taskId: string) => join(root, NOIR_DIR, 'audit', `${taskId}.json`),\n} as const;\n","export const CONTEXT_BLOCK_BEGIN = '<!-- noir:context begin -->';\nexport const CONTEXT_BLOCK_END = '<!-- noir:context end -->';\n","import { readFileSync } from 'node:fs';\nimport { basename } from 'node:path';\nimport { parse as parseYaml } from 'yaml';\nimport { type NoirConfig, parseConfig } from './config.js';\nimport { paths } from './layout.js';\nimport type { ProjectId } from './project-id.js';\n\nexport interface ProjectInfo {\n id: ProjectId;\n name: string;\n root: string;\n config: NoirConfig;\n}\n\nexport function loadProjectInfo(root: string): ProjectInfo {\n let rawId: string;\n let rawConfig: unknown;\n try {\n rawId = readFileSync(paths.projectId(root), 'utf8').trim();\n rawConfig = parseYaml(readFileSync(paths.config(root), 'utf8'));\n } catch {\n throw new Error(`Noir is not initialized in ${root}. Run \\`noir init\\` first.`);\n }\n const config = parseConfig(rawConfig);\n return {\n id: rawId,\n name: config.name ?? basename(root),\n root,\n config,\n };\n}\n","import { randomUUID } from 'node:crypto';\n\n/** Canonical, machine-stable project identity. NEVER a filesystem path. */\nexport type ProjectId = string;\n\nexport function createProjectId(): ProjectId {\n return randomUUID();\n}\n","import { readFileSync } from 'node:fs';\nimport { fileURLToPath } from 'node:url';\n\n// Read the version from THIS package's package.json at module load (NOT a hardcoded\n// constant) so a release bump (scripts/bump-version.mjs) is reflected everywhere\n// `noir` self-identifies — `noir --version`, `noir doctor`/`status`, the MCP\n// initialize handshake — with zero drift between package.json and the binary.\n//\n// `import.meta.url` resolves correctly in every layout the package ships in:\n// src (vitest): packages/core/src/version.ts → ../package.json = packages/core/package.json\n// dist (tsup): packages/core/dist/index.js → ../package.json = packages/core/package.json\n// published: node_modules/@noir-ai/core/dist/... → ../package.json = @noir-ai/core/package.json\n// (npm ALWAYS ships package.json alongside dist/, so the read is safe in the tarball.)\nconst pkgPath = fileURLToPath(new URL('../package.json', import.meta.url));\nexport const NOIR_VERSION: string =\n (JSON.parse(readFileSync(pkgPath, 'utf8')) as { version?: string }).version ?? '0.0.0';\n"],"mappings":"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","names":["readFileSync"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@noir-ai/core",
|
|
3
|
+
"version": "1.0.0-beta.1",
|
|
4
|
+
"description": "Noir core — shared types, the config schema (NoirConfigSchema), the .noir/ layout, and markers.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "agaaaptr",
|
|
7
|
+
"homepage": "https://github.com/agaaaptr/noir#readme",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/agaaaptr/noir.git",
|
|
11
|
+
"directory": "packages/core"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/agaaaptr/noir/issues"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"noir",
|
|
18
|
+
"agent",
|
|
19
|
+
"sdd",
|
|
20
|
+
"spec-driven",
|
|
21
|
+
"config",
|
|
22
|
+
"domain",
|
|
23
|
+
"toolkit"
|
|
24
|
+
],
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=20"
|
|
27
|
+
},
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"access": "public",
|
|
30
|
+
"provenance": true
|
|
31
|
+
},
|
|
32
|
+
"type": "module",
|
|
33
|
+
"main": "./dist/index.js",
|
|
34
|
+
"types": "./dist/index.d.ts",
|
|
35
|
+
"exports": {
|
|
36
|
+
".": {
|
|
37
|
+
"types": "./dist/index.d.ts",
|
|
38
|
+
"import": "./dist/index.js"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"files": [
|
|
42
|
+
"dist",
|
|
43
|
+
"README.md"
|
|
44
|
+
],
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"yaml": "^2.5.0",
|
|
47
|
+
"zod": "^4.2.0"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@types/node": "^26.1.1"
|
|
51
|
+
},
|
|
52
|
+
"scripts": {
|
|
53
|
+
"build": "tsup",
|
|
54
|
+
"typecheck": "tsc --noEmit"
|
|
55
|
+
}
|
|
56
|
+
}
|