@esneiderbravo/speclaw 2.0.12 → 2.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/cli/commands/init.js +3 -2
- package/dist/cli/commands/lawbook.js +4 -1
- package/dist/cli/commands/query.js +2 -2
- package/dist/cli/commands/ship-on-stop.js +46 -0
- package/dist/cli/commands/ship.js +44 -0
- package/dist/cli/commands/update.js +25 -0
- package/dist/cli/index.js +6 -1
- package/dist/cli/lib/help.js +36 -4
- package/dist/modules/compass/db.js +47 -9
- package/dist/modules/compass/explore-rich.js +50 -9
- package/dist/modules/compass/extract.js +220 -0
- package/dist/modules/compass/find-output.js +172 -0
- package/dist/modules/compass/hybrid.js +149 -21
- package/dist/modules/compass/indexer.js +26 -2
- package/dist/modules/compass/query.js +48 -12
- package/dist/modules/compass/register.js +13 -7
- package/dist/modules/cortex/harness.js +17 -4
- package/dist/modules/cortex/status.js +1 -1
- package/dist/modules/foundation/assets/AGENTS.template.md +12 -11
- package/dist/modules/foundation/assets/CLAUDE.template.md +16 -12
- package/dist/modules/foundation/assets/docs/compass.template.md +11 -4
- package/dist/modules/foundation/deps.js +5 -3
- package/dist/modules/foundation/dialects/copilot.js +3 -5
- package/dist/modules/foundation/graph.js +5 -5
- package/dist/modules/foundation/hooks.js +30 -4
- package/dist/modules/foundation/laws.js +5 -0
- package/dist/modules/foundation/scaffold.js +3 -16
- package/dist/modules/lawbook/anchors.js +38 -1
- package/dist/modules/lawbook/assets/commands/cortex.md +1 -1
- package/dist/modules/lawbook/assets/rules/spec-reports-disciplines.md +6 -0
- package/dist/modules/lawbook/assets/rules/spec-tasks-mandatory-steps.md +6 -0
- package/dist/modules/lawbook/assets/skills/cortex/SKILL.md +17 -5
- package/dist/modules/lawbook/bugfix.js +12 -4
- package/dist/modules/lawbook/change-tool.js +7 -0
- package/dist/modules/lawbook/engine.js +3 -0
- package/dist/modules/lawbook/levels.js +35 -4
- package/dist/modules/lawbook/quick.js +17 -6
- package/dist/modules/lawbook/ship.js +325 -0
- package/dist/shared/agents.js +7 -1
- package/dist/shared/mcp.js +6 -4
- package/dist/shared/output-budget.js +36 -12
- package/package.json +1 -1
- package/dist/modules/foundation/assets/workflows/speclaw.yml +0 -26
package/dist/shared/agents.js
CHANGED
|
@@ -37,7 +37,7 @@ export const AGENTS = [
|
|
|
37
37
|
id: "agents",
|
|
38
38
|
label: "Generic (AGENTS.md)",
|
|
39
39
|
ideDir: ".agents",
|
|
40
|
-
linkTargets: ["skills", "agents"],
|
|
40
|
+
linkTargets: ["skills", "commands", "agents", "rules"],
|
|
41
41
|
},
|
|
42
42
|
];
|
|
43
43
|
/**
|
|
@@ -141,6 +141,12 @@ export function configureAgent(projectPath, agentId, report) {
|
|
|
141
141
|
if (!agent)
|
|
142
142
|
throw new Error(`Unknown agent "${agentId}". Known: ${AGENTS.map((a) => a.id).join(", ")}`);
|
|
143
143
|
const ideRoot = path.join(projectPath, agent.ideDir);
|
|
144
|
+
// An agent folder speclaw creates holds only links into the gitignored
|
|
145
|
+
// ai-specs/ and per-developer wiring: keep it out of version control. A
|
|
146
|
+
// folder that already existed is the user's and is left as it is.
|
|
147
|
+
if (!fs.existsSync(ideRoot)) {
|
|
148
|
+
ensureGitignore(projectPath, `${agent.ideDir}/`, `speclaw ${agent.label} links (regenerated by init; never commit)`, report);
|
|
149
|
+
}
|
|
144
150
|
for (const target of agent.linkTargets) {
|
|
145
151
|
const sourceDir = path.join(projectPath, "ai-specs", target);
|
|
146
152
|
if (!fs.existsSync(sourceDir))
|
package/dist/shared/mcp.js
CHANGED
|
@@ -3,15 +3,17 @@ import { toolDefinitionTokens } from "./schema-tokens.js";
|
|
|
3
3
|
import { countWords } from "./tokens.js";
|
|
4
4
|
import { applyTextBudget } from "./output-budget.js";
|
|
5
5
|
/**
|
|
6
|
-
* Wrap a value as an MCP text tool-result,
|
|
6
|
+
* Wrap a value as an MCP text tool-result, applying an output budget.
|
|
7
7
|
*
|
|
8
8
|
* @param value - Payload to return; strings are emitted verbatim, other values are pretty-printed as JSON.
|
|
9
|
-
* @param
|
|
9
|
+
* @param budget - Output mode (`brief` default, unchanged for callers that pass
|
|
10
|
+
* none) or an explicit `{ maxTokens }` cap a handler opted into.
|
|
10
11
|
* @returns An MCP result object with a single text content block.
|
|
11
12
|
*/
|
|
12
|
-
|
|
13
|
+
// Covers: req~find-response-budget~1
|
|
14
|
+
export function text(value, budget = "brief") {
|
|
13
15
|
const raw = typeof value === "string" ? value : JSON.stringify(value, null, 2);
|
|
14
|
-
const budgeted = applyTextBudget(raw,
|
|
16
|
+
const budgeted = applyTextBudget(raw, budget);
|
|
15
17
|
return {
|
|
16
18
|
content: [
|
|
17
19
|
{
|
|
@@ -3,26 +3,43 @@ export const OUTPUT_BUDGET = {
|
|
|
3
3
|
brief: 1500,
|
|
4
4
|
full: 4500,
|
|
5
5
|
};
|
|
6
|
+
/** Brief-mode suffix: the caller can still widen the response with `mode:"full"`. */
|
|
6
7
|
const TRUNCATION_SUFFIX = `\n… [truncated — use mode:"full" or narrow includes]`;
|
|
8
|
+
/** Suffix under the full ceiling or an explicit cap: widening the mode would not help. */
|
|
9
|
+
const FINAL_TRUNCATION_SUFFIX = `\n… [truncated — narrow includes or the query]`;
|
|
7
10
|
/** Stable offline token estimate for budgeting (not a real tokenizer). */
|
|
8
11
|
export function estimateTokens(text) {
|
|
9
12
|
return Math.ceil(text.length / 4);
|
|
10
13
|
}
|
|
11
14
|
/**
|
|
12
|
-
*
|
|
13
|
-
*
|
|
15
|
+
* Token ceiling of a {@link TextBudget}.
|
|
16
|
+
*
|
|
17
|
+
* @param budget - Output mode or explicit cap.
|
|
18
|
+
*/
|
|
19
|
+
export function budgetTokens(budget) {
|
|
20
|
+
return typeof budget === "string" ? OUTPUT_BUDGET[budget] : Math.max(1, budget.maxTokens);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Trim `text` to fit `budget`; returns the possibly shortened text and whether
|
|
24
|
+
* truncation occurred. Under `brief` the suffix suggests `mode:"full"`; under
|
|
25
|
+
* `full` or an explicit cap it never does, since that would not widen anything.
|
|
26
|
+
*
|
|
27
|
+
* @param text - Text to budget.
|
|
28
|
+
* @param budget - Output mode (`brief` default) or `{ maxTokens }`.
|
|
14
29
|
*/
|
|
15
|
-
|
|
16
|
-
|
|
30
|
+
// Covers: req~find-response-budget~1
|
|
31
|
+
export function applyTextBudget(text, budget = "brief") {
|
|
32
|
+
const cap = budgetTokens(budget);
|
|
17
33
|
const tokens = estimateTokens(text);
|
|
18
|
-
if (tokens <=
|
|
34
|
+
if (tokens <= cap)
|
|
19
35
|
return { text, truncated: false, omittedChars: 0 };
|
|
20
|
-
const
|
|
21
|
-
const
|
|
36
|
+
const suffix = budget === "brief" ? TRUNCATION_SUFFIX : FINAL_TRUNCATION_SUFFIX;
|
|
37
|
+
const suffixTokens = estimateTokens(suffix);
|
|
38
|
+
const bodyBudget = Math.max(1, cap - suffixTokens);
|
|
22
39
|
const maxChars = bodyBudget * 4;
|
|
23
40
|
const trimmed = text.slice(0, maxChars);
|
|
24
41
|
return {
|
|
25
|
-
text: trimmed +
|
|
42
|
+
text: trimmed + suffix,
|
|
26
43
|
truncated: true,
|
|
27
44
|
omittedChars: text.length - maxChars,
|
|
28
45
|
};
|
|
@@ -37,8 +54,11 @@ export function applyTextBudget(text, mode = "brief") {
|
|
|
37
54
|
* @param truncated - Collector for explicit truncation records.
|
|
38
55
|
*/
|
|
39
56
|
export function budgetExploreShape(value, mode, truncated) {
|
|
40
|
-
|
|
41
|
-
|
|
57
|
+
// Full mode sizes source to the full-mode token ceiling (~4500 tokens), so a
|
|
58
|
+
// typical long function arrives whole; its hints cannot point at mode:"full".
|
|
59
|
+
const full = mode === "full";
|
|
60
|
+
const maxCallers = full ? 40 : 12;
|
|
61
|
+
const maxSourceLines = full ? 400 : 40;
|
|
42
62
|
const symbol = value.symbol;
|
|
43
63
|
if (symbol && typeof symbol.source === "string") {
|
|
44
64
|
const lines = symbol.source.split("\n");
|
|
@@ -48,7 +68,9 @@ export function budgetExploreShape(value, mode, truncated) {
|
|
|
48
68
|
truncated.push({
|
|
49
69
|
field: "symbol.source",
|
|
50
70
|
omitted,
|
|
51
|
-
hint:
|
|
71
|
+
hint: full
|
|
72
|
+
? "read the remaining lines from the file by startLine/endLine"
|
|
73
|
+
: 'use mode:"full" or omit source from include',
|
|
52
74
|
});
|
|
53
75
|
}
|
|
54
76
|
}
|
|
@@ -63,7 +85,9 @@ export function budgetExploreShape(value, mode, truncated) {
|
|
|
63
85
|
truncated.push({
|
|
64
86
|
field,
|
|
65
87
|
omitted,
|
|
66
|
-
hint:
|
|
88
|
+
hint: full
|
|
89
|
+
? "use compass_impact for the full list"
|
|
90
|
+
: 'use mode:"full" or narrow with include',
|
|
67
91
|
});
|
|
68
92
|
}
|
|
69
93
|
}
|
package/package.json
CHANGED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
# Consumer template. init/update write this only when the path is missing.
|
|
2
|
-
# Never the untrusted PR trigger. The verify job has no secrets.
|
|
3
|
-
name: ⚖️ speclaw
|
|
4
|
-
on: pull_request
|
|
5
|
-
|
|
6
|
-
permissions: {}
|
|
7
|
-
|
|
8
|
-
jobs:
|
|
9
|
-
verify:
|
|
10
|
-
name: ⚖️ Verify laws
|
|
11
|
-
runs-on: ubuntu-latest
|
|
12
|
-
permissions:
|
|
13
|
-
contents: read
|
|
14
|
-
security-events: write
|
|
15
|
-
steps:
|
|
16
|
-
- name: 📥 Checkout
|
|
17
|
-
uses: actions/checkout@v5
|
|
18
|
-
with:
|
|
19
|
-
fetch-depth: 0
|
|
20
|
-
- name: ⚖️ Verify
|
|
21
|
-
uses: esneiderbravo/speclaw@v2
|
|
22
|
-
- name: 🛡️ Upload SARIF
|
|
23
|
-
uses: github/codeql-action/upload-sarif@v4
|
|
24
|
-
if: always()
|
|
25
|
-
with:
|
|
26
|
-
sarif_file: speclaw.sarif
|