@shrkcrft/context 0.1.0-alpha.22 → 0.1.0-alpha.24

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.
@@ -1 +1 @@
1
- {"version":3,"file":"context-builder.d.ts","sourceRoot":"","sources":["../src/context-builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAE3D,OAAO,EAA2B,KAAK,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACrF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAG1D,OAAO,EAAE,qBAAqB,EAAqB,MAAM,2BAA2B,CAAC;AAcrF,wBAAgB,YAAY,CAC1B,UAAU,EAAE,SAAS,eAAe,EAAE,EACtC,OAAO,EAAE,eAAe,GACvB,cAAc,CA4GhB;AAED,OAAO,EAAE,qBAAqB,EAAE,CAAC"}
1
+ {"version":3,"file":"context-builder.d.ts","sourceRoot":"","sources":["../src/context-builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAE3D,OAAO,EAA2B,KAAK,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACrF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAG1D,OAAO,EAAE,qBAAqB,EAAqB,MAAM,2BAA2B,CAAC;AAcrF,wBAAgB,YAAY,CAC1B,UAAU,EAAE,SAAS,eAAe,EAAE,EACtC,OAAO,EAAE,eAAe,GACvB,cAAc,CA0IhB;AAED,OAAO,EAAE,qBAAqB,EAAE,CAAC"}
@@ -21,7 +21,13 @@ export function buildContext(allEntries, request) {
21
21
  if (r.includeRules) {
22
22
  sectionPlans.push({ title: 'Relevant Rules', priority: 90, entries: buckets.rules });
23
23
  }
24
+ // Architecture Decisions rank just above the constraints they justify — a
25
+ // Decision entry whose appliesWhen matches the task is high-signal context an
26
+ // agent must not silently lose (previously these were dropped unless
27
+ // --include-docs was set, then only in the lowest "Reference Docs" bucket).
28
+ sectionPlans.push({ title: 'Architecture Decisions', priority: 82, entries: buckets.decisions });
24
29
  sectionPlans.push({ title: 'Architecture Constraints', priority: 80, entries: buckets.architecture });
30
+ sectionPlans.push({ title: 'Conventions', priority: 75, entries: buckets.conventions });
25
31
  if (r.includePaths) {
26
32
  sectionPlans.push({ title: 'Relevant Path Conventions', priority: 70, entries: buckets.paths });
27
33
  }
@@ -31,10 +37,17 @@ export function buildContext(allEntries, request) {
31
37
  sectionPlans.push({ title: 'Technical Stack', priority: 50, entries: buckets.technical });
32
38
  sectionPlans.push({ title: 'Testing Guidelines', priority: 45, entries: buckets.testing });
33
39
  sectionPlans.push({ title: 'Security Guidelines', priority: 44, entries: buckets.security });
40
+ sectionPlans.push({ title: 'Workflows', priority: 42, entries: buckets.workflows });
34
41
  if (r.includeCommands) {
35
42
  sectionPlans.push({ title: 'Commands', priority: 40, entries: buckets.commands });
36
43
  }
37
44
  sectionPlans.push({ title: 'Current Tasks', priority: 30, entries: buckets.tasks });
45
+ // Default "Project Knowledge" — the misc high-signal types. On by default;
46
+ // suppress with includeKnowledge:false. Distinct from --include-docs, which
47
+ // only ADDS the lowest-value overflow below.
48
+ if (r.includeKnowledge !== false) {
49
+ sectionPlans.push({ title: 'Project Knowledge', priority: 25, entries: buckets.knowledge });
50
+ }
38
51
  if (r.includeDocs) {
39
52
  sectionPlans.push({ title: 'Reference Docs', priority: 10, entries: buckets.docs });
40
53
  }
@@ -54,34 +67,44 @@ export function buildContext(allEntries, request) {
54
67
  const sections = [];
55
68
  const omitted = [];
56
69
  let used = 0;
57
- function tryAddSection(title, body, entryIds) {
70
+ // Sections at/above this priority degrade by TRUNCATION rather than vanishing
71
+ // when they don't fit. Plans are processed in descending priority order, so
72
+ // the current plan is always the highest-priority one still unplaced; a big
73
+ // critical section (e.g. Agent Actions, 92) must not be dropped whole while a
74
+ // small low-priority section is kept. Below the threshold, a non-fitting
75
+ // section is omitted (and a later, smaller one may still fit).
76
+ const PRUNE_PROTECT_PRIORITY = 80;
77
+ function tryAddSection(title, body, entryIds, priority) {
58
78
  const tokens = estimateTokens(body);
59
- if (used + tokens > maxTokens && sections.length > 0) {
60
- omitted.push(title);
79
+ if (used + tokens <= maxTokens) {
80
+ sections.push({ title, body, entryIds, tokens });
81
+ used += tokens;
61
82
  return;
62
83
  }
63
- if (used + tokens > maxTokens) {
64
- // Still emit, but mark truncated.
65
- const ratio = (maxTokens - used) / tokens;
84
+ const remaining = maxTokens - used;
85
+ // Truncate-to-fit when this is the first section (nothing emitted yet) or a
86
+ // protected high-priority one provided there is any budget left.
87
+ if (remaining > 0 && (sections.length === 0 || priority >= PRUNE_PROTECT_PRIORITY)) {
88
+ const ratio = remaining / tokens;
66
89
  const truncatedBody = body.slice(0, Math.max(0, Math.floor(body.length * ratio))) + '\n…[truncated]';
67
90
  const truncTokens = estimateTokens(truncatedBody);
68
91
  sections.push({ title, body: truncatedBody, entryIds, tokens: truncTokens, truncated: true });
69
92
  used += truncTokens;
70
93
  return;
71
94
  }
72
- sections.push({ title, body, entryIds, tokens });
73
- used += tokens;
95
+ omitted.push(title);
74
96
  }
75
97
  for (const plan of sectionPlans) {
76
98
  if (plan.title === 'Project Overview' && r.projectOverview) {
77
- tryAddSection('Project Overview', r.projectOverview.trim(), []);
99
+ tryAddSection('Project Overview', r.projectOverview.trim(), [], plan.priority);
78
100
  continue;
79
101
  }
80
102
  // Composite section with a precomputed body (e.g. Agent Actions) — added in
81
103
  // priority order with its contributing-entry ids.
82
104
  if (plan.body !== undefined) {
83
- if (plan.body.length > 0)
84
- tryAddSection(plan.title, plan.body, aggregated.contributingEntries);
105
+ if (plan.body.length > 0) {
106
+ tryAddSection(plan.title, plan.body, aggregated.contributingEntries, plan.priority);
107
+ }
85
108
  continue;
86
109
  }
87
110
  if (plan.entries.length === 0)
@@ -91,7 +114,7 @@ export function buildContext(allEntries, request) {
91
114
  maxContentChars: 1500,
92
115
  });
93
116
  const ids = plan.entries.map((e) => e.id);
94
- tryAddSection(plan.title, body, ids);
117
+ tryAddSection(plan.title, body, ids, plan.priority);
95
118
  }
96
119
  const fullBody = sections
97
120
  .map((s) => `## ${s.title}${s.truncated ? ' (truncated)' : ''}\n\n${s.body}`)
@@ -11,10 +11,27 @@ export interface IContextRequest {
11
11
  includeRules?: boolean;
12
12
  includePaths?: boolean;
13
13
  includeDocs?: boolean;
14
+ /**
15
+ * Emit the default "Project Knowledge" section for genuinely-misc high-signal
16
+ * types (feature / business / decision-adjacent / …). Defaults to `true`; set
17
+ * `false` to suppress that section without losing the dedicated ones.
18
+ */
19
+ includeKnowledge?: boolean;
14
20
  includeOverview?: boolean;
15
21
  includeWarnings?: boolean;
16
22
  includeCommands?: boolean;
17
23
  projectOverview?: string;
24
+ /**
25
+ * Optional per-entry score boost (e.g. pack search-tuning). Applied as a
26
+ * stable re-rank of the relevance results so a boosted entry can cross the
27
+ * per-section cap. Returns a delta (0 = no boost). Kept as a plain structural
28
+ * callback so the context layer needs no dependency on the inspector.
29
+ */
30
+ boostFor?: (entry: {
31
+ readonly id: string;
32
+ readonly type?: unknown;
33
+ readonly tags?: readonly string[];
34
+ }) => number;
18
35
  }
19
- export declare const DEFAULT_CONTEXT_REQUEST: Required<Pick<IContextRequest, 'maxTokens' | 'includeExamples' | 'includeTemplates' | 'includeRules' | 'includePaths' | 'includeDocs' | 'includeOverview' | 'includeWarnings' | 'includeCommands'>>;
36
+ export declare const DEFAULT_CONTEXT_REQUEST: Required<Pick<IContextRequest, 'maxTokens' | 'includeExamples' | 'includeTemplates' | 'includeRules' | 'includePaths' | 'includeDocs' | 'includeKnowledge' | 'includeOverview' | 'includeWarnings' | 'includeCommands'>>;
20
37
  //# sourceMappingURL=context-request.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"context-request.d.ts","sourceRoot":"","sources":["../src/context-request.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1B,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,eAAO,MAAM,uBAAuB,EAAE,QAAQ,CAC5C,IAAI,CACF,eAAe,EACb,WAAW,GACX,iBAAiB,GACjB,kBAAkB,GAClB,cAAc,GACd,cAAc,GACd,aAAa,GACb,iBAAiB,GACjB,iBAAiB,GACjB,iBAAiB,CACpB,CAWF,CAAC"}
1
+ {"version":3,"file":"context-request.d.ts","sourceRoot":"","sources":["../src/context-request.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1B,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;KAAE,KAAK,MAAM,CAAC;CACnH;AAED,eAAO,MAAM,uBAAuB,EAAE,QAAQ,CAC5C,IAAI,CACF,eAAe,EACb,WAAW,GACX,iBAAiB,GACjB,kBAAkB,GAClB,cAAc,GACd,cAAc,GACd,aAAa,GACb,kBAAkB,GAClB,iBAAiB,GACjB,iBAAiB,GACjB,iBAAiB,CACpB,CAYF,CAAC"}
@@ -5,6 +5,7 @@ export const DEFAULT_CONTEXT_REQUEST = {
5
5
  includeRules: true,
6
6
  includePaths: true,
7
7
  includeDocs: false,
8
+ includeKnowledge: true,
8
9
  includeOverview: true,
9
10
  includeWarnings: true,
10
11
  includeCommands: false,
@@ -5,11 +5,21 @@ export interface SelectedEntries {
5
5
  paths: IKnowledgeEntry[];
6
6
  templates: IKnowledgeEntry[];
7
7
  architecture: IKnowledgeEntry[];
8
+ decisions: IKnowledgeEntry[];
9
+ conventions: IKnowledgeEntry[];
8
10
  technical: IKnowledgeEntry[];
9
11
  warnings: IKnowledgeEntry[];
10
12
  commands: IKnowledgeEntry[];
13
+ workflows: IKnowledgeEntry[];
11
14
  testing: IKnowledgeEntry[];
12
15
  security: IKnowledgeEntry[];
16
+ /**
17
+ * Genuinely-misc high-signal types that don't map to a dedicated section
18
+ * (feature / business / environment / dependency / deployment / integration /
19
+ * custom / …). Surfaced by default in the "Project Knowledge" section — these
20
+ * used to be dropped entirely unless `--include-docs` was set.
21
+ */
22
+ knowledge: IKnowledgeEntry[];
13
23
  docs: IKnowledgeEntry[];
14
24
  tasks: IKnowledgeEntry[];
15
25
  }
@@ -1 +1 @@
1
- {"version":3,"file":"relevance-selector.d.ts","sourceRoot":"","sources":["../src/relevance-selector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAE3D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAG5D,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,eAAe,EAAE,CAAC;IACzB,KAAK,EAAE,eAAe,EAAE,CAAC;IACzB,SAAS,EAAE,eAAe,EAAE,CAAC;IAC7B,YAAY,EAAE,eAAe,EAAE,CAAC;IAChC,SAAS,EAAE,eAAe,EAAE,CAAC;IAC7B,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,IAAI,EAAE,eAAe,EAAE,CAAC;IACxB,KAAK,EAAE,eAAe,EAAE,CAAC;CAC1B;AAeD,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,SAAS,eAAe,EAAE,EACtC,OAAO,EAAE,eAAe,EACxB,eAAe,SAAI,GAClB,eAAe,CAgDjB"}
1
+ {"version":3,"file":"relevance-selector.d.ts","sourceRoot":"","sources":["../src/relevance-selector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAE3D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAG5D,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,eAAe,EAAE,CAAC;IACzB,KAAK,EAAE,eAAe,EAAE,CAAC;IACzB,SAAS,EAAE,eAAe,EAAE,CAAC;IAC7B,YAAY,EAAE,eAAe,EAAE,CAAC;IAChC,SAAS,EAAE,eAAe,EAAE,CAAC;IAC7B,WAAW,EAAE,eAAe,EAAE,CAAC;IAC/B,SAAS,EAAE,eAAe,EAAE,CAAC;IAC7B,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,SAAS,EAAE,eAAe,EAAE,CAAC;IAC7B,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B;;;;;OAKG;IACH,SAAS,EAAE,eAAe,EAAE,CAAC;IAC7B,IAAI,EAAE,eAAe,EAAE,CAAC;IACxB,KAAK,EAAE,eAAe,EAAE,CAAC;CAC1B;AAkBD,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,SAAS,eAAe,EAAE,EACtC,OAAO,EAAE,eAAe,EACxB,eAAe,SAAI,GAClB,eAAe,CA0EjB"}
@@ -5,9 +5,12 @@ const TYPE_BUCKETS = {
5
5
  path: 'paths',
6
6
  template: 'templates',
7
7
  architecture: 'architecture',
8
+ decision: 'decisions',
9
+ convention: 'conventions',
8
10
  technical: 'technical',
9
11
  warning: 'warnings',
10
12
  command: 'commands',
13
+ workflow: 'workflows',
11
14
  testing: 'testing',
12
15
  security: 'security',
13
16
  task: 'tasks',
@@ -34,29 +37,53 @@ export function selectRelevantEntries(allEntries, request, limitPerSection = 5)
34
37
  tags,
35
38
  appliesWhen,
36
39
  });
40
+ // Pack search-tuning boost: stable-sort so boosted entries (delta > 0) bubble
41
+ // ahead of unboosted ones (delta = 0) while preserving the relevance order
42
+ // among equal boosts. Lets a boosted rule/knowledge entry cross the
43
+ // per-section cap, mirroring how the task ranker applies the same tuning.
44
+ const boostFor = request.boostFor;
45
+ const ranked = boostFor
46
+ ? [...searchAll].sort((a, b) => {
47
+ const sa = a.score + (boostFor(a.entry) ?? 0);
48
+ const sb = b.score + (boostFor(b.entry) ?? 0);
49
+ return sb - sa || a.entry.id.localeCompare(b.entry.id);
50
+ })
51
+ : searchAll;
37
52
  const buckets = {
38
53
  rules: [],
39
54
  paths: [],
40
55
  templates: [],
41
56
  architecture: [],
57
+ decisions: [],
58
+ conventions: [],
42
59
  technical: [],
43
60
  warnings: [],
44
61
  commands: [],
62
+ workflows: [],
45
63
  testing: [],
46
64
  security: [],
65
+ knowledge: [],
47
66
  docs: [],
48
67
  tasks: [],
49
68
  };
50
- for (const r of searchAll) {
69
+ for (const r of ranked) {
51
70
  const typeKey = String(r.entry.type).toLowerCase();
52
71
  const bucketKey = TYPE_BUCKETS[typeKey];
53
72
  if (bucketKey) {
54
73
  if (buckets[bucketKey].length < limitPerSection)
55
74
  buckets[bucketKey].push(r.entry);
75
+ continue;
56
76
  }
57
- else if (request.includeDocs) {
58
- if (buckets.docs.length < limitPerSection)
59
- buckets.docs.push(r.entry);
77
+ // Unmapped type. Route to the default "Project Knowledge" bucket so a
78
+ // high-signal entry (e.g. a Decision/Feature whose appliesWhen matches the
79
+ // task) reaches the agent by default. `--include-docs` only ADDS the
80
+ // lowest-value overflow into "Reference Docs" — it no longer gates ALL
81
+ // unmapped types behind an off-by-default flag.
82
+ if (buckets.knowledge.length < limitPerSection) {
83
+ buckets.knowledge.push(r.entry);
84
+ }
85
+ else if (request.includeDocs && buckets.docs.length < limitPerSection) {
86
+ buckets.docs.push(r.entry);
60
87
  }
61
88
  }
62
89
  return buckets;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shrkcrft/context",
3
- "version": "0.1.0-alpha.22",
3
+ "version": "0.1.0-alpha.24",
4
4
  "description": "SharkCraft AI context builder: token-budgeted relevance retrieval for tasks.",
5
5
  "license": "MIT",
6
6
  "author": "SharkCraft contributors",
@@ -44,11 +44,11 @@
44
44
  "typecheck": "tsc --noEmit -p tsconfig.json"
45
45
  },
46
46
  "dependencies": {
47
- "@shrkcrft/core": "^0.1.0-alpha.22",
48
- "@shrkcrft/knowledge": "^0.1.0-alpha.22",
49
- "@shrkcrft/rules": "^0.1.0-alpha.22",
50
- "@shrkcrft/paths": "^0.1.0-alpha.22",
51
- "@shrkcrft/templates": "^0.1.0-alpha.22"
47
+ "@shrkcrft/core": "^0.1.0-alpha.24",
48
+ "@shrkcrft/knowledge": "^0.1.0-alpha.24",
49
+ "@shrkcrft/rules": "^0.1.0-alpha.24",
50
+ "@shrkcrft/paths": "^0.1.0-alpha.24",
51
+ "@shrkcrft/templates": "^0.1.0-alpha.24"
52
52
  },
53
53
  "publishConfig": {
54
54
  "access": "public"