faceted-prompting 0.0.0 → 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Masanobu Naruse
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 CHANGED
@@ -1,5 +1,72 @@
1
1
  # faceted-prompting
2
2
 
3
- Faceted Prompting - A design pattern for composing structured AI prompts from multiple facets.
3
+ Structured prompt composition for LLMs using the Faceted Prompting pattern.
4
4
 
5
- Work in progress.
5
+ ## Overview
6
+
7
+ Faceted Prompting organizes LLM prompts into distinct facets:
8
+
9
+ | Facet | Role | Placement |
10
+ |-------|------|-----------|
11
+ | **Persona** | WHO — defines the agent's identity | System prompt |
12
+ | **Policy** | HOW — coding standards, rules | User message |
13
+ | **Knowledge** | WHAT TO KNOW — domain context | User message |
14
+ | **Instruction** | WHAT TO DO — the task itself | User message |
15
+
16
+ ## Install
17
+
18
+ ```bash
19
+ npm install faceted-prompting
20
+ ```
21
+
22
+ ## Quick Start
23
+
24
+ ```typescript
25
+ import { compose } from 'faceted-prompting';
26
+
27
+ const result = compose(
28
+ {
29
+ persona: { body: 'You are a senior TypeScript developer.' },
30
+ policies: [{ body: 'Follow clean code principles. No any types.' }],
31
+ knowledge: [{ body: 'The project uses Vitest for testing.' }],
32
+ instruction: { body: 'Implement a retry function with exponential backoff.' },
33
+ },
34
+ { contextMaxChars: 8000 },
35
+ );
36
+
37
+ // result.systemPrompt → persona content
38
+ // result.userMessage → policy + knowledge + instruction (in order)
39
+ ```
40
+
41
+ ## API
42
+
43
+ ### `compose(facets, options)`
44
+
45
+ Composes a `FacetSet` into a `ComposedPrompt` with `systemPrompt` and `userMessage`.
46
+
47
+ ### `FileDataEngine`
48
+
49
+ File-system backed facet loader. Reads `{root}/{kind}/{key}.md`.
50
+
51
+ ```typescript
52
+ import { FileDataEngine } from 'faceted-prompting';
53
+
54
+ const engine = new FileDataEngine('./prompts');
55
+ const persona = await engine.resolve('personas', 'coder');
56
+ ```
57
+
58
+ ### `CompositeDataEngine`
59
+
60
+ Chains multiple engines with first-match-wins resolution.
61
+
62
+ ### `renderTemplate(template, vars)`
63
+
64
+ Minimal template engine supporting `{{#if}}...{{else}}...{{/if}}` and `{{variable}}` substitution.
65
+
66
+ ### `escapeTemplateChars(str)`
67
+
68
+ Escapes curly braces to prevent template injection.
69
+
70
+ ## License
71
+
72
+ MIT
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Facet composition — the core placement rule.
3
+ *
4
+ * system prompt: persona only (WHO)
5
+ * user message: policy + knowledge + instruction (HOW / WHAT TO KNOW / WHAT TO DO)
6
+ *
7
+ * This module has ZERO dependencies on TAKT internals.
8
+ */
9
+ import type { FacetSet, ComposedPrompt, ComposeOptions } from './types.js';
10
+ /**
11
+ * Compose facets into an LLM-ready prompt according to Faceted Prompting
12
+ * placement rules.
13
+ *
14
+ * - persona → systemPrompt
15
+ * - policy / knowledge / instruction → userMessage (in that order)
16
+ */
17
+ export declare function compose(facets: FacetSet, options: ComposeOptions): ComposedPrompt;
18
+ //# sourceMappingURL=compose.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compose.d.ts","sourceRoot":"","sources":["../src/compose.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAG3E;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,cAAc,GAAG,cAAc,CAoCjF"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Facet composition — the core placement rule.
3
+ *
4
+ * system prompt: persona only (WHO)
5
+ * user message: policy + knowledge + instruction (HOW / WHAT TO KNOW / WHAT TO DO)
6
+ *
7
+ * This module has ZERO dependencies on TAKT internals.
8
+ */
9
+ import { prepareKnowledgeContent, preparePolicyContent } from './truncation.js';
10
+ /**
11
+ * Compose facets into an LLM-ready prompt according to Faceted Prompting
12
+ * placement rules.
13
+ *
14
+ * - persona → systemPrompt
15
+ * - policy / knowledge / instruction → userMessage (in that order)
16
+ */
17
+ export function compose(facets, options) {
18
+ const systemPrompt = facets.persona?.body ?? '';
19
+ const userParts = [];
20
+ // Policy (HOW)
21
+ if (facets.policies && facets.policies.length > 0) {
22
+ const joined = facets.policies.map(p => p.body).join('\n\n---\n\n');
23
+ const sourcePath = facets.policies.length === 1
24
+ ? facets.policies[0].sourcePath
25
+ : undefined;
26
+ userParts.push(preparePolicyContent(joined, options.contextMaxChars, sourcePath));
27
+ }
28
+ // Knowledge (WHAT TO KNOW)
29
+ if (facets.knowledge && facets.knowledge.length > 0) {
30
+ const joined = facets.knowledge.map(k => k.body).join('\n\n---\n\n');
31
+ const sourcePath = facets.knowledge.length === 1
32
+ ? facets.knowledge[0].sourcePath
33
+ : undefined;
34
+ userParts.push(prepareKnowledgeContent(joined, options.contextMaxChars, sourcePath));
35
+ }
36
+ // Instruction (WHAT TO DO)
37
+ if (facets.instruction) {
38
+ userParts.push(facets.instruction.body);
39
+ }
40
+ return {
41
+ systemPrompt,
42
+ userMessage: userParts.join('\n\n'),
43
+ };
44
+ }
45
+ //# sourceMappingURL=compose.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compose.js","sourceRoot":"","sources":["../src/compose.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAEhF;;;;;;GAMG;AACH,MAAM,UAAU,OAAO,CAAC,MAAgB,EAAE,OAAuB;IAC/D,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC;IAEhD,MAAM,SAAS,GAAa,EAAE,CAAC;IAE/B,eAAe;IACf,IAAI,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAClD,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACpE,MAAM,UAAU,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC;YAC7C,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAE,CAAC,UAAU;YAChC,CAAC,CAAC,SAAS,CAAC;QACd,SAAS,CAAC,IAAI,CACZ,oBAAoB,CAAC,MAAM,EAAE,OAAO,CAAC,eAAe,EAAE,UAAU,CAAC,CAClE,CAAC;IACJ,CAAC;IAED,2BAA2B;IAC3B,IAAI,MAAM,CAAC,SAAS,IAAI,MAAM,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACpD,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACrE,MAAM,UAAU,GAAG,MAAM,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC;YAC9C,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAE,CAAC,UAAU;YACjC,CAAC,CAAC,SAAS,CAAC;QACd,SAAS,CAAC,IAAI,CACZ,uBAAuB,CAAC,MAAM,EAAE,OAAO,CAAC,eAAe,EAAE,UAAU,CAAC,CACrE,CAAC;IACJ,CAAC;IAED,2BAA2B;IAC3B,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;QACvB,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IAC1C,CAAC;IAED,OAAO;QACL,YAAY;QACZ,WAAW,EAAE,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC;KACpC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * DataEngine — abstract interface for facet data retrieval.
3
+ *
4
+ * Compose logic depends only on this interface; callers wire
5
+ * concrete implementations (FileDataEngine, SqliteDataEngine, etc.).
6
+ *
7
+ * This module depends only on node:fs, node:path.
8
+ */
9
+ import type { FacetKind, FacetContent } from './types.js';
10
+ /**
11
+ * Abstract interface for facet data retrieval.
12
+ *
13
+ * Methods return Promises so that implementations backed by
14
+ * async I/O (database, network) can be used without changes.
15
+ */
16
+ export interface DataEngine {
17
+ /**
18
+ * Resolve a single facet by kind and key (name without extension).
19
+ * Returns undefined if the facet does not exist.
20
+ */
21
+ resolve(kind: FacetKind, key: string): Promise<FacetContent | undefined>;
22
+ /** List available facet keys for a given kind. */
23
+ list(kind: FacetKind): Promise<string[]>;
24
+ }
25
+ /**
26
+ * File-system backed DataEngine.
27
+ *
28
+ * Resolves facets from a single root directory using the convention:
29
+ * {root}/{kind}/{key}.md
30
+ */
31
+ export declare class FileDataEngine implements DataEngine {
32
+ private readonly root;
33
+ constructor(root: string);
34
+ resolve(kind: FacetKind, key: string): Promise<FacetContent | undefined>;
35
+ list(kind: FacetKind): Promise<string[]>;
36
+ }
37
+ /**
38
+ * Chains multiple DataEngines with first-match-wins resolution.
39
+ *
40
+ * resolve() returns the first non-undefined result.
41
+ * list() returns deduplicated keys from all engines.
42
+ */
43
+ export declare class CompositeDataEngine implements DataEngine {
44
+ private readonly engines;
45
+ constructor(engines: readonly DataEngine[]);
46
+ resolve(kind: FacetKind, key: string): Promise<FacetContent | undefined>;
47
+ list(kind: FacetKind): Promise<string[]>;
48
+ }
49
+ //# sourceMappingURL=data-engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data-engine.d.ts","sourceRoot":"","sources":["../src/data-engine.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAW1D;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB;;;OAGG;IACH,OAAO,CAAC,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC,CAAC;IAEzE,kDAAkD;IAClD,IAAI,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;CAC1C;AAED;;;;;GAKG;AACH,qBAAa,cAAe,YAAW,UAAU;IACnC,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,MAAM;IAEnC,OAAO,CAAC,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC;IAQxE,IAAI,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;CAQ/C;AAED;;;;;GAKG;AACH,qBAAa,mBAAoB,YAAW,UAAU;IACxC,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,SAAS,UAAU,EAAE;IAMrD,OAAO,CAAC,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC;IAQxE,IAAI,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;CAc/C"}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * DataEngine — abstract interface for facet data retrieval.
3
+ *
4
+ * Compose logic depends only on this interface; callers wire
5
+ * concrete implementations (FileDataEngine, SqliteDataEngine, etc.).
6
+ *
7
+ * This module depends only on node:fs, node:path.
8
+ */
9
+ import { existsSync, readFileSync, readdirSync } from 'node:fs';
10
+ import { join } from 'node:path';
11
+ /** Plural-kind to directory name mapping (identity for all current kinds). */
12
+ const KIND_DIR = {
13
+ personas: 'personas',
14
+ policies: 'policies',
15
+ knowledge: 'knowledge',
16
+ instructions: 'instructions',
17
+ 'output-contracts': 'output-contracts',
18
+ };
19
+ /**
20
+ * File-system backed DataEngine.
21
+ *
22
+ * Resolves facets from a single root directory using the convention:
23
+ * {root}/{kind}/{key}.md
24
+ */
25
+ export class FileDataEngine {
26
+ root;
27
+ constructor(root) {
28
+ this.root = root;
29
+ }
30
+ async resolve(kind, key) {
31
+ const dir = KIND_DIR[kind];
32
+ const filePath = join(this.root, dir, `${key}.md`);
33
+ if (!existsSync(filePath))
34
+ return undefined;
35
+ const body = readFileSync(filePath, 'utf-8');
36
+ return { body, sourcePath: filePath };
37
+ }
38
+ async list(kind) {
39
+ const dir = KIND_DIR[kind];
40
+ const dirPath = join(this.root, dir);
41
+ if (!existsSync(dirPath))
42
+ return [];
43
+ return readdirSync(dirPath)
44
+ .filter(f => f.endsWith('.md'))
45
+ .map(f => f.slice(0, -3));
46
+ }
47
+ }
48
+ /**
49
+ * Chains multiple DataEngines with first-match-wins resolution.
50
+ *
51
+ * resolve() returns the first non-undefined result.
52
+ * list() returns deduplicated keys from all engines.
53
+ */
54
+ export class CompositeDataEngine {
55
+ engines;
56
+ constructor(engines) {
57
+ this.engines = engines;
58
+ if (engines.length === 0) {
59
+ throw new Error('CompositeDataEngine requires at least one engine');
60
+ }
61
+ }
62
+ async resolve(kind, key) {
63
+ for (const engine of this.engines) {
64
+ const result = await engine.resolve(kind, key);
65
+ if (result !== undefined)
66
+ return result;
67
+ }
68
+ return undefined;
69
+ }
70
+ async list(kind) {
71
+ const seen = new Set();
72
+ const result = [];
73
+ for (const engine of this.engines) {
74
+ const keys = await engine.list(kind);
75
+ for (const key of keys) {
76
+ if (!seen.has(key)) {
77
+ seen.add(key);
78
+ result.push(key);
79
+ }
80
+ }
81
+ }
82
+ return result;
83
+ }
84
+ }
85
+ //# sourceMappingURL=data-engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data-engine.js","sourceRoot":"","sources":["../src/data-engine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Template injection prevention.
3
+ *
4
+ * Escapes curly braces in dynamic content so they are not
5
+ * interpreted as template variables by the template engine.
6
+ *
7
+ * This module has ZERO dependencies on TAKT internals.
8
+ */
9
+ /**
10
+ * Replace ASCII curly braces with full-width equivalents
11
+ * to prevent template variable injection in user-supplied content.
12
+ */
13
+ export declare function escapeTemplateChars(str: string): string;
14
+ //# sourceMappingURL=escape.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"escape.d.ts","sourceRoot":"","sources":["../src/escape.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEvD"}
package/dist/escape.js ADDED
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Template injection prevention.
3
+ *
4
+ * Escapes curly braces in dynamic content so they are not
5
+ * interpreted as template variables by the template engine.
6
+ *
7
+ * This module has ZERO dependencies on TAKT internals.
8
+ */
9
+ /**
10
+ * Replace ASCII curly braces with full-width equivalents
11
+ * to prevent template variable injection in user-supplied content.
12
+ */
13
+ export function escapeTemplateChars(str) {
14
+ return str.replace(/\{/g, '\uff5b').replace(/\}/g, '\uff5d');
15
+ }
16
+ //# sourceMappingURL=escape.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"escape.js","sourceRoot":"","sources":["../src/escape.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,GAAW;IAC7C,OAAO,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;AAC/D,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * faceted-prompting — Public API
3
+ *
4
+ * Re-exports all public types, interfaces, and functions.
5
+ * Consumers should import from this module only.
6
+ */
7
+ export type { FacetKind, FacetContent, FacetSet, ComposedPrompt, ComposeOptions, } from './types.js';
8
+ export { compose } from './compose.js';
9
+ export type { DataEngine } from './data-engine.js';
10
+ export { FileDataEngine, CompositeDataEngine } from './data-engine.js';
11
+ export { trimContextContent, renderConflictNotice, prepareKnowledgeContent, preparePolicyContent, } from './truncation.js';
12
+ export { renderTemplate } from './template.js';
13
+ export { escapeTemplateChars } from './escape.js';
14
+ export type { PieceSections } from './resolve.js';
15
+ export { isResourcePath, resolveFacetPath, resolveFacetByName, resolveResourcePath, resolveResourceContent, resolveRefToContent, resolveRefList, resolveSectionMap, extractPersonaDisplayName, resolvePersona, } from './resolve.js';
16
+ export type { ScopeRef } from './scope.js';
17
+ export { isScopeRef, parseScopeRef, resolveScopeRef, validateScopeOwner, validateScopeRepo, validateScopeFacetName, } from './scope.js';
18
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,YAAY,EACV,SAAS,EACT,YAAY,EACZ,QAAQ,EACR,cAAc,EACd,cAAc,GACf,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAGvC,YAAY,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAGvE,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAG/C,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAGlD,YAAY,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,EACnB,sBAAsB,EACtB,mBAAmB,EACnB,cAAc,EACd,iBAAiB,EACjB,yBAAyB,EACzB,cAAc,GACf,MAAM,cAAc,CAAC;AAGtB,YAAY,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EACL,UAAU,EACV,aAAa,EACb,eAAe,EACf,kBAAkB,EAClB,iBAAiB,EACjB,sBAAsB,GACvB,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,18 @@
1
+ /**
2
+ * faceted-prompting — Public API
3
+ *
4
+ * Re-exports all public types, interfaces, and functions.
5
+ * Consumers should import from this module only.
6
+ */
7
+ // Compose
8
+ export { compose } from './compose.js';
9
+ export { FileDataEngine, CompositeDataEngine } from './data-engine.js';
10
+ // Truncation
11
+ export { trimContextContent, renderConflictNotice, prepareKnowledgeContent, preparePolicyContent, } from './truncation.js';
12
+ // Template engine
13
+ export { renderTemplate } from './template.js';
14
+ // Escape
15
+ export { escapeTemplateChars } from './escape.js';
16
+ export { isResourcePath, resolveFacetPath, resolveFacetByName, resolveResourcePath, resolveResourceContent, resolveRefToContent, resolveRefList, resolveSectionMap, extractPersonaDisplayName, resolvePersona, } from './resolve.js';
17
+ export { isScopeRef, parseScopeRef, resolveScopeRef, validateScopeOwner, validateScopeRepo, validateScopeFacetName, } from './scope.js';
18
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAWH,UAAU;AACV,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAIvC,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAEvE,aAAa;AACb,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,iBAAiB,CAAC;AAEzB,kBAAkB;AAClB,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/C,SAAS;AACT,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAIlD,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,EACnB,sBAAsB,EACtB,mBAAmB,EACnB,cAAc,EACd,iBAAiB,EACjB,yBAAyB,EACzB,cAAc,GACf,MAAM,cAAc,CAAC;AAItB,OAAO,EACL,UAAU,EACV,aAAa,EACb,eAAe,EACf,kBAAkB,EAClB,iBAAiB,EACjB,sBAAsB,GACvB,MAAM,YAAY,CAAC"}
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Facet reference resolution utilities.
3
+ *
4
+ * Resolves facet names / paths / content from section maps
5
+ * and candidate directories. Directory construction is delegated
6
+ * to the caller (TAKT provides project/global/builtin dirs).
7
+ *
8
+ * This module depends only on node:fs, node:os, node:path.
9
+ */
10
+ /** Pre-resolved section maps passed to movement normalization. */
11
+ export interface PieceSections {
12
+ /** Persona name -> file path (raw, not content-resolved) */
13
+ personas?: Record<string, string>;
14
+ /** Policy name -> resolved content */
15
+ resolvedPolicies?: Record<string, string>;
16
+ /** Knowledge name -> resolved content */
17
+ resolvedKnowledge?: Record<string, string>;
18
+ /** Instruction name -> resolved content */
19
+ resolvedInstructions?: Record<string, string>;
20
+ /** Report format name -> resolved content */
21
+ resolvedReportFormats?: Record<string, string>;
22
+ }
23
+ /**
24
+ * Check if a spec looks like a resource path (vs. a facet name).
25
+ * Paths start with './', '../', '/', '~' or end with '.md'.
26
+ */
27
+ export declare function isResourcePath(spec: string): boolean;
28
+ /**
29
+ * Resolve a facet name to its file path by scanning candidate directories.
30
+ *
31
+ * The caller builds the candidate list (e.g. project/.takt/{kind},
32
+ * ~/.takt/{kind}, builtins/{lang}/{kind}) and passes it in.
33
+ *
34
+ * @returns Absolute file path if found, undefined otherwise.
35
+ */
36
+ export declare function resolveFacetPath(name: string, candidateDirs: readonly string[]): string | undefined;
37
+ /**
38
+ * Resolve a facet name to its file content via candidate directories.
39
+ *
40
+ * @returns File content if found, undefined otherwise.
41
+ */
42
+ export declare function resolveFacetByName(name: string, candidateDirs: readonly string[]): string | undefined;
43
+ /** Resolve a resource spec to an absolute file path. */
44
+ export declare function resolveResourcePath(spec: string, pieceDir: string): string;
45
+ /**
46
+ * Resolve a resource spec to its file content.
47
+ * If the spec ends with .md and the file exists, returns file content.
48
+ * Otherwise returns the spec as-is (treated as inline content).
49
+ */
50
+ export declare function resolveResourceContent(spec: string | undefined, pieceDir: string): string | undefined;
51
+ /**
52
+ * Resolve a section reference to content.
53
+ * Looks up ref in resolvedMap first, then falls back to path resolution.
54
+ * If candidateDirs are provided and ref is a name (not a path),
55
+ * falls back to facet resolution via candidate directories.
56
+ */
57
+ export declare function resolveRefToContent(ref: string, resolvedMap: Record<string, string> | undefined, pieceDir: string, candidateDirs?: readonly string[]): string | undefined;
58
+ /** Resolve multiple references to content strings (for fields that accept string | string[]). */
59
+ export declare function resolveRefList(refs: string | string[] | undefined, resolvedMap: Record<string, string> | undefined, pieceDir: string, candidateDirs?: readonly string[]): string[] | undefined;
60
+ /** Resolve a piece-level section map (each value resolved to file content or inline). */
61
+ export declare function resolveSectionMap(raw: Record<string, string> | undefined, pieceDir: string): Record<string, string> | undefined;
62
+ /** Extract display name from persona path (e.g., "coder.md" -> "coder"). */
63
+ export declare function extractPersonaDisplayName(personaPath: string): string;
64
+ /**
65
+ * Resolve persona from YAML field to spec + absolute path.
66
+ *
67
+ * Candidate directories for name-based lookup are provided by the caller.
68
+ */
69
+ export declare function resolvePersona(rawPersona: string | undefined, sections: PieceSections, pieceDir: string, candidateDirs?: readonly string[]): {
70
+ personaSpec?: string;
71
+ personaPath?: string;
72
+ };
73
+ //# sourceMappingURL=resolve.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../src/resolve.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,kEAAkE;AAClE,MAAM,WAAW,aAAa;IAC5B,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,sCAAsC;IACtC,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,yCAAyC;IACzC,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3C,2CAA2C;IAC3C,oBAAoB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9C,6CAA6C;IAC7C,qBAAqB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChD;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAQpD;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,aAAa,EAAE,SAAS,MAAM,EAAE,GAC/B,MAAM,GAAG,SAAS,CAQpB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,aAAa,EAAE,SAAS,MAAM,EAAE,GAC/B,MAAM,GAAG,SAAS,CAMpB;AAED,wDAAwD;AACxD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAK1E;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,QAAQ,EAAE,MAAM,GACf,MAAM,GAAG,SAAS,CAOpB;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,EAC/C,QAAQ,EAAE,MAAM,EAChB,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,GAChC,MAAM,GAAG,SAAS,CAcpB;AAED,iGAAiG;AACjG,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,EACnC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,EAC/C,QAAQ,EAAE,MAAM,EAChB,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,GAChC,MAAM,EAAE,GAAG,SAAS,CAStB;AAED,yFAAyF;AACzF,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,EACvC,QAAQ,EAAE,MAAM,GACf,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAQpC;AAED,4EAA4E;AAC5E,wBAAgB,yBAAyB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAErE;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,QAAQ,EAAE,aAAa,EACvB,QAAQ,EAAE,MAAM,EAChB,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,GAChC;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CA8BhD"}
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Facet reference resolution utilities.
3
+ *
4
+ * Resolves facet names / paths / content from section maps
5
+ * and candidate directories. Directory construction is delegated
6
+ * to the caller (TAKT provides project/global/builtin dirs).
7
+ *
8
+ * This module depends only on node:fs, node:os, node:path.
9
+ */
10
+ import { readFileSync, existsSync } from 'node:fs';
11
+ import { homedir } from 'node:os';
12
+ import { join, basename } from 'node:path';
13
+ /**
14
+ * Check if a spec looks like a resource path (vs. a facet name).
15
+ * Paths start with './', '../', '/', '~' or end with '.md'.
16
+ */
17
+ export function isResourcePath(spec) {
18
+ return (spec.startsWith('./') ||
19
+ spec.startsWith('../') ||
20
+ spec.startsWith('/') ||
21
+ spec.startsWith('~') ||
22
+ spec.endsWith('.md'));
23
+ }
24
+ /**
25
+ * Resolve a facet name to its file path by scanning candidate directories.
26
+ *
27
+ * The caller builds the candidate list (e.g. project/.takt/{kind},
28
+ * ~/.takt/{kind}, builtins/{lang}/{kind}) and passes it in.
29
+ *
30
+ * @returns Absolute file path if found, undefined otherwise.
31
+ */
32
+ export function resolveFacetPath(name, candidateDirs) {
33
+ for (const dir of candidateDirs) {
34
+ const filePath = join(dir, `${name}.md`);
35
+ if (existsSync(filePath)) {
36
+ return filePath;
37
+ }
38
+ }
39
+ return undefined;
40
+ }
41
+ /**
42
+ * Resolve a facet name to its file content via candidate directories.
43
+ *
44
+ * @returns File content if found, undefined otherwise.
45
+ */
46
+ export function resolveFacetByName(name, candidateDirs) {
47
+ const filePath = resolveFacetPath(name, candidateDirs);
48
+ if (filePath) {
49
+ return readFileSync(filePath, 'utf-8');
50
+ }
51
+ return undefined;
52
+ }
53
+ /** Resolve a resource spec to an absolute file path. */
54
+ export function resolveResourcePath(spec, pieceDir) {
55
+ if (spec.startsWith('./'))
56
+ return join(pieceDir, spec.slice(2));
57
+ if (spec.startsWith('~'))
58
+ return join(homedir(), spec.slice(1));
59
+ if (spec.startsWith('/'))
60
+ return spec;
61
+ return join(pieceDir, spec);
62
+ }
63
+ /**
64
+ * Resolve a resource spec to its file content.
65
+ * If the spec ends with .md and the file exists, returns file content.
66
+ * Otherwise returns the spec as-is (treated as inline content).
67
+ */
68
+ export function resolveResourceContent(spec, pieceDir) {
69
+ if (spec == null)
70
+ return undefined;
71
+ if (spec.endsWith('.md')) {
72
+ const resolved = resolveResourcePath(spec, pieceDir);
73
+ if (existsSync(resolved))
74
+ return readFileSync(resolved, 'utf-8');
75
+ }
76
+ return spec;
77
+ }
78
+ /**
79
+ * Resolve a section reference to content.
80
+ * Looks up ref in resolvedMap first, then falls back to path resolution.
81
+ * If candidateDirs are provided and ref is a name (not a path),
82
+ * falls back to facet resolution via candidate directories.
83
+ */
84
+ export function resolveRefToContent(ref, resolvedMap, pieceDir, candidateDirs) {
85
+ const mapped = resolvedMap?.[ref];
86
+ if (mapped)
87
+ return mapped;
88
+ if (isResourcePath(ref)) {
89
+ return resolveResourceContent(ref, pieceDir);
90
+ }
91
+ if (candidateDirs) {
92
+ const facetContent = resolveFacetByName(ref, candidateDirs);
93
+ if (facetContent !== undefined)
94
+ return facetContent;
95
+ }
96
+ return resolveResourceContent(ref, pieceDir);
97
+ }
98
+ /** Resolve multiple references to content strings (for fields that accept string | string[]). */
99
+ export function resolveRefList(refs, resolvedMap, pieceDir, candidateDirs) {
100
+ if (refs == null)
101
+ return undefined;
102
+ const list = Array.isArray(refs) ? refs : [refs];
103
+ const contents = [];
104
+ for (const ref of list) {
105
+ const content = resolveRefToContent(ref, resolvedMap, pieceDir, candidateDirs);
106
+ if (content)
107
+ contents.push(content);
108
+ }
109
+ return contents.length > 0 ? contents : undefined;
110
+ }
111
+ /** Resolve a piece-level section map (each value resolved to file content or inline). */
112
+ export function resolveSectionMap(raw, pieceDir) {
113
+ if (!raw)
114
+ return undefined;
115
+ const resolved = {};
116
+ for (const [name, value] of Object.entries(raw)) {
117
+ const content = resolveResourceContent(value, pieceDir);
118
+ if (content)
119
+ resolved[name] = content;
120
+ }
121
+ return Object.keys(resolved).length > 0 ? resolved : undefined;
122
+ }
123
+ /** Extract display name from persona path (e.g., "coder.md" -> "coder"). */
124
+ export function extractPersonaDisplayName(personaPath) {
125
+ return basename(personaPath, '.md');
126
+ }
127
+ /**
128
+ * Resolve persona from YAML field to spec + absolute path.
129
+ *
130
+ * Candidate directories for name-based lookup are provided by the caller.
131
+ */
132
+ export function resolvePersona(rawPersona, sections, pieceDir, candidateDirs) {
133
+ if (!rawPersona)
134
+ return {};
135
+ // If section map has explicit mapping, use it (path-based)
136
+ const sectionMapping = sections.personas?.[rawPersona];
137
+ if (sectionMapping) {
138
+ const resolved = resolveResourcePath(sectionMapping, pieceDir);
139
+ const personaPath = existsSync(resolved) ? resolved : undefined;
140
+ return { personaSpec: sectionMapping, personaPath };
141
+ }
142
+ // If rawPersona is a path, resolve it directly
143
+ if (isResourcePath(rawPersona)) {
144
+ const resolved = resolveResourcePath(rawPersona, pieceDir);
145
+ const personaPath = existsSync(resolved) ? resolved : undefined;
146
+ return { personaSpec: rawPersona, personaPath };
147
+ }
148
+ // Name-based: try candidate directories
149
+ if (candidateDirs) {
150
+ const filePath = resolveFacetPath(rawPersona, candidateDirs);
151
+ if (filePath) {
152
+ return { personaSpec: rawPersona, personaPath: filePath };
153
+ }
154
+ }
155
+ // Fallback: try as relative path from pieceDir
156
+ const resolved = resolveResourcePath(rawPersona, pieceDir);
157
+ const personaPath = existsSync(resolved) ? resolved : undefined;
158
+ return { personaSpec: rawPersona, personaPath };
159
+ }
160
+ //# sourceMappingURL=resolve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve.js","sourceRoot":"","sources":["../src/resolve.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * @scope reference resolution utilities for TAKT repertoire packages.
3
+ *
4
+ * Provides:
5
+ * - isScopeRef(): detect @{owner}/{repo}/{facet-name} format
6
+ * - parseScopeRef(): parse and normalize components
7
+ * - resolveScopeRef(): build file path in repertoire directory
8
+ * - validateScopeOwner/Repo/FacetName(): name constraint validation
9
+ */
10
+ /** Parsed components of an @scope reference. */
11
+ export interface ScopeRef {
12
+ /** GitHub owner (lowercase). */
13
+ owner: string;
14
+ /** Repository name (lowercase). */
15
+ repo: string;
16
+ /** Facet name. */
17
+ name: string;
18
+ }
19
+ /**
20
+ * Return true if the string is an @{owner}/{repo}/{facet-name} scope reference.
21
+ */
22
+ export declare function isScopeRef(ref: string): boolean;
23
+ /**
24
+ * Parse an @scope reference into its components.
25
+ * Normalizes owner and repo to lowercase.
26
+ *
27
+ * @param ref - e.g. "@nrslib/takt-fullstack/expert-coder"
28
+ */
29
+ export declare function parseScopeRef(ref: string): ScopeRef;
30
+ /**
31
+ * Resolve a scope reference to a file path in the repertoire directory.
32
+ *
33
+ * Path: {repertoireDir}/@{owner}/{repo}/facets/{facetType}/{name}.md
34
+ *
35
+ * @param scopeRef - parsed scope reference
36
+ * @param facetType - e.g. "personas", "policies", "knowledge"
37
+ * @param repertoireDir - root repertoire directory (e.g. ~/.takt/repertoire)
38
+ * @returns Absolute path to the facet file.
39
+ */
40
+ export declare function resolveScopeRef(scopeRef: ScopeRef, facetType: string, repertoireDir: string): string;
41
+ /** Validate owner name: must match /^[a-z0-9][a-z0-9-]*$/ */
42
+ export declare function validateScopeOwner(owner: string): void;
43
+ /** Validate repo name: must match /^[a-z0-9][a-z0-9._-]*$/ */
44
+ export declare function validateScopeRepo(repo: string): void;
45
+ /** Validate facet name: must match /^[a-z0-9][a-z0-9-]*$/ */
46
+ export declare function validateScopeFacetName(name: string): void;
47
+ //# sourceMappingURL=scope.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scope.d.ts","sourceRoot":"","sources":["../src/scope.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,gDAAgD;AAChD,MAAM,WAAW,QAAQ;IACvB,gCAAgC;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,mCAAmC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,kBAAkB;IAClB,IAAI,EAAE,MAAM,CAAC;CACd;AAKD;;GAEG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAE/C;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,CAYnD;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,QAAQ,EAClB,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,GACpB,MAAM,CASR;AAED,6DAA6D;AAC7D,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAMtD;AAED,8DAA8D;AAC9D,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAMpD;AAED,6DAA6D;AAC7D,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAMzD"}
package/dist/scope.js ADDED
@@ -0,0 +1,69 @@
1
+ /**
2
+ * @scope reference resolution utilities for TAKT repertoire packages.
3
+ *
4
+ * Provides:
5
+ * - isScopeRef(): detect @{owner}/{repo}/{facet-name} format
6
+ * - parseScopeRef(): parse and normalize components
7
+ * - resolveScopeRef(): build file path in repertoire directory
8
+ * - validateScopeOwner/Repo/FacetName(): name constraint validation
9
+ */
10
+ import { join } from 'node:path';
11
+ /** Matches @{owner}/{repo}/{facet-name} format. */
12
+ const SCOPE_REF_PATTERN = /^@[^/]+\/[^/]+\/[^/]+$/;
13
+ /**
14
+ * Return true if the string is an @{owner}/{repo}/{facet-name} scope reference.
15
+ */
16
+ export function isScopeRef(ref) {
17
+ return SCOPE_REF_PATTERN.test(ref);
18
+ }
19
+ /**
20
+ * Parse an @scope reference into its components.
21
+ * Normalizes owner and repo to lowercase.
22
+ *
23
+ * @param ref - e.g. "@nrslib/takt-fullstack/expert-coder"
24
+ */
25
+ export function parseScopeRef(ref) {
26
+ const withoutAt = ref.slice(1);
27
+ const firstSlash = withoutAt.indexOf('/');
28
+ const owner = withoutAt.slice(0, firstSlash).toLowerCase();
29
+ const rest = withoutAt.slice(firstSlash + 1);
30
+ const secondSlash = rest.indexOf('/');
31
+ const repo = rest.slice(0, secondSlash).toLowerCase();
32
+ const name = rest.slice(secondSlash + 1);
33
+ validateScopeOwner(owner);
34
+ validateScopeRepo(repo);
35
+ validateScopeFacetName(name);
36
+ return { owner, repo, name };
37
+ }
38
+ /**
39
+ * Resolve a scope reference to a file path in the repertoire directory.
40
+ *
41
+ * Path: {repertoireDir}/@{owner}/{repo}/facets/{facetType}/{name}.md
42
+ *
43
+ * @param scopeRef - parsed scope reference
44
+ * @param facetType - e.g. "personas", "policies", "knowledge"
45
+ * @param repertoireDir - root repertoire directory (e.g. ~/.takt/repertoire)
46
+ * @returns Absolute path to the facet file.
47
+ */
48
+ export function resolveScopeRef(scopeRef, facetType, repertoireDir) {
49
+ return join(repertoireDir, `@${scopeRef.owner}`, scopeRef.repo, 'facets', facetType, `${scopeRef.name}.md`);
50
+ }
51
+ /** Validate owner name: must match /^[a-z0-9][a-z0-9-]*$/ */
52
+ export function validateScopeOwner(owner) {
53
+ if (!/^[a-z0-9][a-z0-9-]*$/.test(owner)) {
54
+ throw new Error(`Invalid scope owner: "${owner}". Must match /^[a-z0-9][a-z0-9-]*$/ (lowercase alphanumeric and hyphens, not starting with hyphen).`);
55
+ }
56
+ }
57
+ /** Validate repo name: must match /^[a-z0-9][a-z0-9._-]*$/ */
58
+ export function validateScopeRepo(repo) {
59
+ if (!/^[a-z0-9][a-z0-9._-]*$/.test(repo)) {
60
+ throw new Error(`Invalid scope repo: "${repo}". Must match /^[a-z0-9][a-z0-9._-]*$/ (lowercase alphanumeric, hyphens, dots, underscores, not starting with hyphen).`);
61
+ }
62
+ }
63
+ /** Validate facet name: must match /^[a-z0-9][a-z0-9-]*$/ */
64
+ export function validateScopeFacetName(name) {
65
+ if (!/^[a-z0-9][a-z0-9-]*$/.test(name)) {
66
+ throw new Error(`Invalid scope facet name: "${name}". Must match /^[a-z0-9][a-z0-9-]*$/ (lowercase alphanumeric and hyphens, not starting with hyphen).`);
67
+ }
68
+ }
69
+ //# sourceMappingURL=scope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scope.js","sourceRoot":"","sources":["../src/scope.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAYjC,mDAAmD;AACnD,MAAM,iBAAiB,GAAG,wBAAwB,CAAC;AAEnD;;GAEG;AACH,MAAM,UAAU,UAAU,CAAC,GAAW;IACpC,OAAO,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACrC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,GAAW;IACvC,MAAM,SAAS,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC/B,MAAM,UAAU,GAAG,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC1C,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,WAAW,EAAE,CAAC;IAC3D,MAAM,IAAI,GAAG,SAAS,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC;IAC7C,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACtC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,WAAW,EAAE,CAAC;IACtD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC;IACzC,kBAAkB,CAAC,KAAK,CAAC,CAAC;IAC1B,iBAAiB,CAAC,IAAI,CAAC,CAAC;IACxB,sBAAsB,CAAC,IAAI,CAAC,CAAC;IAC7B,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AAC/B,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,eAAe,CAC7B,QAAkB,EAClB,SAAiB,EACjB,aAAqB;IAErB,OAAO,IAAI,CACT,aAAa,EACb,IAAI,QAAQ,CAAC,KAAK,EAAE,EACpB,QAAQ,CAAC,IAAI,EACb,QAAQ,EACR,SAAS,EACT,GAAG,QAAQ,CAAC,IAAI,KAAK,CACtB,CAAC;AACJ,CAAC;AAED,6DAA6D;AAC7D,MAAM,UAAU,kBAAkB,CAAC,KAAa;IAC9C,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACxC,MAAM,IAAI,KAAK,CACb,yBAAyB,KAAK,sGAAsG,CACrI,CAAC;IACJ,CAAC;AACH,CAAC;AAED,8DAA8D;AAC9D,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,IAAI,CAAC,wBAAwB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACzC,MAAM,IAAI,KAAK,CACb,wBAAwB,IAAI,wHAAwH,CACrJ,CAAC;IACJ,CAAC;AACH,CAAC;AAED,6DAA6D;AAC7D,MAAM,UAAU,sBAAsB,CAAC,IAAY;IACjD,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACvC,MAAM,IAAI,KAAK,CACb,8BAA8B,IAAI,sGAAsG,CACzI,CAAC;IACJ,CAAC;AACH,CAAC"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Minimal template engine for Markdown prompt templates.
3
+ *
4
+ * Supports:
5
+ * - {{#if variable}}...{{else}}...{{/if}} conditional blocks (no nesting)
6
+ * - {{variableName}} substitution
7
+ *
8
+ * This module has ZERO dependencies on TAKT internals.
9
+ */
10
+ /**
11
+ * Process {{#if variable}}...{{else}}...{{/if}} conditional blocks.
12
+ *
13
+ * A variable is truthy when it is a non-empty string or boolean true.
14
+ * Nesting is NOT supported.
15
+ */
16
+ export declare function processConditionals(template: string, vars: Record<string, string | boolean>): string;
17
+ /**
18
+ * Replace {{variableName}} placeholders with values from vars.
19
+ * Undefined or false variables are replaced with empty string.
20
+ * True is replaced with the string "true".
21
+ */
22
+ export declare function substituteVariables(template: string, vars: Record<string, string | boolean>): string;
23
+ /**
24
+ * Render a template string by processing conditionals then substituting variables.
25
+ */
26
+ export declare function renderTemplate(template: string, vars: Record<string, string | boolean>): string;
27
+ //# sourceMappingURL=template.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template.d.ts","sourceRoot":"","sources":["../src/template.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,GACrC,MAAM,CAcR;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,GACrC,MAAM,CAUR;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,GACrC,MAAM,CAGR"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Minimal template engine for Markdown prompt templates.
3
+ *
4
+ * Supports:
5
+ * - {{#if variable}}...{{else}}...{{/if}} conditional blocks (no nesting)
6
+ * - {{variableName}} substitution
7
+ *
8
+ * This module has ZERO dependencies on TAKT internals.
9
+ */
10
+ /**
11
+ * Process {{#if variable}}...{{else}}...{{/if}} conditional blocks.
12
+ *
13
+ * A variable is truthy when it is a non-empty string or boolean true.
14
+ * Nesting is NOT supported.
15
+ */
16
+ export function processConditionals(template, vars) {
17
+ return template.replace(/\{\{#if\s+(\w+)\}\}([\s\S]*?)\{\{\/if\}\}/g, (_match, varName, body) => {
18
+ const value = vars[varName];
19
+ const isTruthy = value !== undefined && value !== false && value !== '';
20
+ const elseIndex = body.indexOf('{{else}}');
21
+ if (isTruthy) {
22
+ return elseIndex >= 0 ? body.slice(0, elseIndex) : body;
23
+ }
24
+ return elseIndex >= 0 ? body.slice(elseIndex + '{{else}}'.length) : '';
25
+ });
26
+ }
27
+ /**
28
+ * Replace {{variableName}} placeholders with values from vars.
29
+ * Undefined or false variables are replaced with empty string.
30
+ * True is replaced with the string "true".
31
+ */
32
+ export function substituteVariables(template, vars) {
33
+ return template.replace(/\{\{(\w+)\}\}/g, (_match, varName) => {
34
+ const value = vars[varName];
35
+ if (value === undefined || value === false)
36
+ return '';
37
+ if (value === true)
38
+ return 'true';
39
+ return value;
40
+ });
41
+ }
42
+ /**
43
+ * Render a template string by processing conditionals then substituting variables.
44
+ */
45
+ export function renderTemplate(template, vars) {
46
+ const afterConditionals = processConditionals(template, vars);
47
+ return substituteVariables(afterConditionals, vars);
48
+ }
49
+ //# sourceMappingURL=template.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template.js","sourceRoot":"","sources":["../src/template.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CACjC,QAAgB,EAChB,IAAsC;IAEtC,OAAO,QAAQ,CAAC,OAAO,CACrB,4CAA4C,EAC5C,CAAC,MAAM,EAAE,OAAe,EAAE,IAAY,EAAU,EAAE;QAChD,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC;QAC5B,MAAM,QAAQ,GAAG,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,KAAK,IAAI,KAAK,KAAK,EAAE,CAAC;QAExE,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QAC3C,IAAI,QAAQ,EAAE,CAAC;YACb,OAAO,SAAS,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAC1D,CAAC;QACD,OAAO,SAAS,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACzE,CAAC,CACF,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CACjC,QAAgB,EAChB,IAAsC;IAEtC,OAAO,QAAQ,CAAC,OAAO,CACrB,gBAAgB,EAChB,CAAC,MAAM,EAAE,OAAe,EAAE,EAAE;QAC1B,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC;QAC5B,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,KAAK;YAAE,OAAO,EAAE,CAAC;QACtD,IAAI,KAAK,KAAK,IAAI;YAAE,OAAO,MAAM,CAAC;QAClC,OAAO,KAAK,CAAC;IACf,CAAC,CACF,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAC5B,QAAgB,EAChB,IAAsC;IAEtC,MAAM,iBAAiB,GAAG,mBAAmB,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAC9D,OAAO,mBAAmB,CAAC,iBAAiB,EAAE,IAAI,CAAC,CAAC;AACtD,CAAC"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Context truncation for knowledge and policy facets.
3
+ *
4
+ * When facet content exceeds a character limit, it is trimmed and
5
+ * annotated with source-path metadata so the LLM can consult the
6
+ * original file.
7
+ *
8
+ * This module has ZERO dependencies on TAKT internals.
9
+ */
10
+ interface PreparedContextBlock {
11
+ readonly content: string;
12
+ readonly truncated: boolean;
13
+ }
14
+ /**
15
+ * Trim content to a maximum character length, appending a
16
+ * "...TRUNCATED..." marker when truncation occurs.
17
+ */
18
+ export declare function trimContextContent(content: string, maxChars: number): PreparedContextBlock;
19
+ /**
20
+ * Standard notice appended to knowledge and policy blocks.
21
+ */
22
+ export declare function renderConflictNotice(): string;
23
+ /**
24
+ * Prepare a knowledge facet for inclusion in a prompt.
25
+ *
26
+ * Trims to maxChars, appends truncation notice and source path if available.
27
+ */
28
+ export declare function prepareKnowledgeContent(content: string, maxChars: number, sourcePath?: string): string;
29
+ /**
30
+ * Prepare a policy facet for inclusion in a prompt.
31
+ *
32
+ * Trims to maxChars, appends authoritative-source notice and source path if available.
33
+ */
34
+ export declare function preparePolicyContent(content: string, maxChars: number, sourcePath?: string): string;
35
+ export {};
36
+ //# sourceMappingURL=truncation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"truncation.d.ts","sourceRoot":"","sources":["../src/truncation.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,UAAU,oBAAoB;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC7B;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GACf,oBAAoB,CAQtB;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,UAAU,CAAC,EAAE,MAAM,GAClB,MAAM,CAcR;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,UAAU,CAAC,EAAE,MAAM,GAClB,MAAM,CAcR"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Context truncation for knowledge and policy facets.
3
+ *
4
+ * When facet content exceeds a character limit, it is trimmed and
5
+ * annotated with source-path metadata so the LLM can consult the
6
+ * original file.
7
+ *
8
+ * This module has ZERO dependencies on TAKT internals.
9
+ */
10
+ /**
11
+ * Trim content to a maximum character length, appending a
12
+ * "...TRUNCATED..." marker when truncation occurs.
13
+ */
14
+ export function trimContextContent(content, maxChars) {
15
+ if (content.length <= maxChars) {
16
+ return { content, truncated: false };
17
+ }
18
+ return {
19
+ content: `${content.slice(0, maxChars)}\n...TRUNCATED...`,
20
+ truncated: true,
21
+ };
22
+ }
23
+ /**
24
+ * Standard notice appended to knowledge and policy blocks.
25
+ */
26
+ export function renderConflictNotice() {
27
+ return 'If prompt content conflicts with source files, source files take precedence.';
28
+ }
29
+ /**
30
+ * Prepare a knowledge facet for inclusion in a prompt.
31
+ *
32
+ * Trims to maxChars, appends truncation notice and source path if available.
33
+ */
34
+ export function prepareKnowledgeContent(content, maxChars, sourcePath) {
35
+ const prepared = trimContextContent(content, maxChars);
36
+ const lines = [prepared.content];
37
+ if (prepared.truncated && sourcePath) {
38
+ lines.push('', `Knowledge is truncated. You MUST consult the source files before making decisions. Source: ${sourcePath}`);
39
+ }
40
+ if (sourcePath) {
41
+ lines.push('', `Knowledge Source: ${sourcePath}`);
42
+ }
43
+ lines.push('', renderConflictNotice());
44
+ return lines.join('\n');
45
+ }
46
+ /**
47
+ * Prepare a policy facet for inclusion in a prompt.
48
+ *
49
+ * Trims to maxChars, appends authoritative-source notice and source path if available.
50
+ */
51
+ export function preparePolicyContent(content, maxChars, sourcePath) {
52
+ const prepared = trimContextContent(content, maxChars);
53
+ const lines = [prepared.content];
54
+ if (prepared.truncated && sourcePath) {
55
+ lines.push('', `Policy is authoritative. If truncated, you MUST read the full policy file and follow it strictly. Source: ${sourcePath}`);
56
+ }
57
+ if (sourcePath) {
58
+ lines.push('', `Policy Source: ${sourcePath}`);
59
+ }
60
+ lines.push('', renderConflictNotice());
61
+ return lines.join('\n');
62
+ }
63
+ //# sourceMappingURL=truncation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"truncation.js","sourceRoot":"","sources":["../src/truncation.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAOH;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAChC,OAAe,EACf,QAAgB;IAEhB,IAAI,OAAO,CAAC,MAAM,IAAI,QAAQ,EAAE,CAAC;QAC/B,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;IACvC,CAAC;IACD,OAAO;QACL,OAAO,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,mBAAmB;QACzD,SAAS,EAAE,IAAI;KAChB,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB;IAClC,OAAO,8EAA8E,CAAC;AACxF,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,uBAAuB,CACrC,OAAe,EACf,QAAgB,EAChB,UAAmB;IAEnB,MAAM,QAAQ,GAAG,kBAAkB,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IACvD,MAAM,KAAK,GAAa,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC3C,IAAI,QAAQ,CAAC,SAAS,IAAI,UAAU,EAAE,CAAC;QACrC,KAAK,CAAC,IAAI,CACR,EAAE,EACF,8FAA8F,UAAU,EAAE,CAC3G,CAAC;IACJ,CAAC;IACD,IAAI,UAAU,EAAE,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,qBAAqB,UAAU,EAAE,CAAC,CAAC;IACpD,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,oBAAoB,EAAE,CAAC,CAAC;IACvC,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAClC,OAAe,EACf,QAAgB,EAChB,UAAmB;IAEnB,MAAM,QAAQ,GAAG,kBAAkB,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IACvD,MAAM,KAAK,GAAa,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC3C,IAAI,QAAQ,CAAC,SAAS,IAAI,UAAU,EAAE,CAAC;QACrC,KAAK,CAAC,IAAI,CACR,EAAE,EACF,6GAA6G,UAAU,EAAE,CAC1H,CAAC;IACJ,CAAC;IACD,IAAI,UAAU,EAAE,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,kBAAkB,UAAU,EAAE,CAAC,CAAC;IACjD,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,oBAAoB,EAAE,CAAC,CAAC;IACvC,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Core type definitions for Faceted Prompting.
3
+ *
4
+ * Defines the vocabulary of facets (persona, policy, knowledge, instruction,
5
+ * output-contract) and the structures used by compose() and DataEngine.
6
+ *
7
+ * This module has ZERO dependencies on TAKT internals.
8
+ */
9
+ /** Plural directory names used in facet resolution. */
10
+ export type FacetKind = 'personas' | 'policies' | 'knowledge' | 'instructions' | 'output-contracts';
11
+ /** A single piece of facet content with optional metadata. */
12
+ export interface FacetContent {
13
+ /** Raw text body of the facet. */
14
+ readonly body: string;
15
+ /** Filesystem path the content was loaded from, if applicable. */
16
+ readonly sourcePath?: string;
17
+ }
18
+ /**
19
+ * A complete set of resolved facet contents to be composed.
20
+ *
21
+ * All fields are optional — a FacetSet may contain only a subset of facets.
22
+ */
23
+ export interface FacetSet {
24
+ readonly persona?: FacetContent;
25
+ readonly policies?: readonly FacetContent[];
26
+ readonly knowledge?: readonly FacetContent[];
27
+ readonly instruction?: FacetContent;
28
+ }
29
+ /**
30
+ * The output of compose(): facet content assigned to LLM message slots.
31
+ *
32
+ * persona → systemPrompt
33
+ * policy + knowledge + instruction → userMessage
34
+ */
35
+ export interface ComposedPrompt {
36
+ readonly systemPrompt: string;
37
+ readonly userMessage: string;
38
+ }
39
+ /** Options controlling compose() behaviour. */
40
+ export interface ComposeOptions {
41
+ /** Maximum character length for knowledge/policy content before truncation. */
42
+ readonly contextMaxChars: number;
43
+ }
44
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,uDAAuD;AACvD,MAAM,MAAM,SAAS,GACjB,UAAU,GACV,UAAU,GACV,WAAW,GACX,cAAc,GACd,kBAAkB,CAAC;AAEvB,8DAA8D;AAC9D,MAAM,WAAW,YAAY;IAC3B,kCAAkC;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,kEAAkE;IAClE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;GAIG;AACH,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,OAAO,CAAC,EAAE,YAAY,CAAC;IAChC,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,YAAY,EAAE,CAAC;IAC5C,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,YAAY,EAAE,CAAC;IAC7C,QAAQ,CAAC,WAAW,CAAC,EAAE,YAAY,CAAC;CACrC;AAED;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED,+CAA+C;AAC/C,MAAM,WAAW,cAAc;IAC7B,+EAA+E;IAC/E,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;CAClC"}
package/dist/types.js ADDED
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Core type definitions for Faceted Prompting.
3
+ *
4
+ * Defines the vocabulary of facets (persona, policy, knowledge, instruction,
5
+ * output-contract) and the structures used by compose() and DataEngine.
6
+ *
7
+ * This module has ZERO dependencies on TAKT internals.
8
+ */
9
+ export {};
10
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
package/package.json CHANGED
@@ -1,11 +1,42 @@
1
1
  {
2
2
  "name": "faceted-prompting",
3
- "version": "0.0.0",
4
- "description": "Faceted Prompting - A design pattern for composing structured AI prompts from multiple facets (persona, policy, knowledge, instruction)",
5
- "author": "nrslib",
3
+ "version": "0.1.0",
4
+ "description": "Faceted Prompting \u2014 structured prompt composition for LLMs",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "scripts": {
18
+ "build": "tsc",
19
+ "watch": "tsc --watch",
20
+ "test": "vitest run",
21
+ "test:watch": "vitest",
22
+ "lint": "eslint src/",
23
+ "prepublishOnly": "npm run build && npm run test"
24
+ },
25
+ "keywords": [
26
+ "prompt",
27
+ "llm",
28
+ "faceted-prompting",
29
+ "prompt-engineering",
30
+ "ai"
31
+ ],
6
32
  "license": "MIT",
7
- "repository": {
8
- "type": "git",
9
- "url": "https://github.com/nrslib/faceted-prompting"
33
+ "engines": {
34
+ "node": ">=18"
35
+ },
36
+ "devDependencies": {
37
+ "@types/node": "^22.19.11",
38
+ "eslint": "^9.0.0",
39
+ "typescript": "^5.9.3",
40
+ "vitest": "^3.2.4"
10
41
  }
11
- }
42
+ }