@trieungoctam/speckit 0.3.0 → 0.3.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/README.md +11 -1
- package/dist/adapters/claude-code-adapter.js +16 -0
- package/dist/adapters/codex-adapter.js +2 -0
- package/dist/adapters/cursor-adapter.js +23 -0
- package/dist/cli.js +13 -0
- package/dist/commands/doctor.js +10 -3
- package/dist/commands/permissions.d.ts +8 -0
- package/dist/commands/permissions.js +18 -0
- package/dist/commands/validate.d.ts +6 -0
- package/dist/commands/validate.js +17 -0
- package/dist/core/permission-auditor.d.ts +10 -0
- package/dist/core/permission-auditor.js +65 -0
- package/dist/core/permission-policy.d.ts +6 -0
- package/dist/core/permission-policy.js +93 -0
- package/dist/core/scaffold.js +2 -0
- package/dist/core/skill-catalog.d.ts +1 -0
- package/dist/core/skill-catalog.js +3 -0
- package/dist/core/workflow-contract.d.ts +7 -0
- package/dist/core/workflow-contract.js +38 -0
- package/dist/core/workflow-validator.d.ts +6 -0
- package/dist/core/workflow-validator.js +133 -0
- package/docs/development-roadmap.md +3 -1
- package/docs/permission-rules-research.md +265 -0
- package/docs/product-contract.md +3 -1
- package/docs/project-changelog.md +16 -0
- package/docs/use-cases.md +206 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -21,6 +21,8 @@ npx @trieungoctam/speckit@latest context .speckit/stories/<story>.md
|
|
|
21
21
|
npx @trieungoctam/speckit@latest sync
|
|
22
22
|
npx @trieungoctam/speckit@latest sprint plan
|
|
23
23
|
npx @trieungoctam/speckit@latest graph triage --json
|
|
24
|
+
npx @trieungoctam/speckit@latest validate --json
|
|
25
|
+
npx @trieungoctam/speckit@latest permissions audit --path .env --json
|
|
24
26
|
npx @trieungoctam/speckit@latest ready .speckit/stories/<story>.md
|
|
25
27
|
npx @trieungoctam/speckit@latest session checkpoint --note "red complete"
|
|
26
28
|
npx @trieungoctam/speckit@latest review
|
|
@@ -68,9 +70,11 @@ For implementation stories, red-green-refactor evidence is mandatory. A story is
|
|
|
68
70
|
| `speckit init --enterprise` | Add flow, tool policy, and prompt harness files on top of the shared runtime. |
|
|
69
71
|
| `speckit init --ide <name>` | Generate shared Speckit runtime plus one adapter: `claude-code`, `codex`, `antigravity`, `opencode`, or `cursor`. |
|
|
70
72
|
| `speckit doctor` | Report required tools, optional integrations, test commands, and adapter readiness. |
|
|
71
|
-
| `speckit doctor --deep` | Verify core enterprise harness files. |
|
|
73
|
+
| `speckit doctor --deep` | Verify core enterprise harness files and workflow contract checks. |
|
|
74
|
+
| `speckit validate` | Validate flow order, core skills, super-agent routing, prompts, and installed adapter contracts. |
|
|
72
75
|
| `speckit start "<idea>"` | Create a durable session handoff and current context. |
|
|
73
76
|
| `speckit memory refresh` | Create durable project memory files for long-running agent work. |
|
|
77
|
+
| `speckit permissions audit` | Check a path or command against the Speckit permission policy. |
|
|
74
78
|
| `speckit session start` | Create or resume the active session state. |
|
|
75
79
|
| `speckit session checkpoint` | Append a long-session checkpoint and artifact log entry. |
|
|
76
80
|
| `speckit session compact` | Create an anchored summary for resume or handoff. |
|
|
@@ -105,6 +109,12 @@ Speckit generates native instruction/config files for:
|
|
|
105
109
|
|
|
106
110
|
See `docs/adapters.md` for exact output paths.
|
|
107
111
|
|
|
112
|
+
## Guides
|
|
113
|
+
|
|
114
|
+
- `docs/use-cases.md` covers setup, migration, quick changes, full planning, long sessions, TDD, graph automation, review, CI, and troubleshooting.
|
|
115
|
+
- `docs/workflow-model.md` describes the quick and full workflow lanes.
|
|
116
|
+
- `docs/spec-quality-gates.md` describes validation gates for release readiness.
|
|
117
|
+
|
|
108
118
|
## Validation
|
|
109
119
|
|
|
110
120
|
```bash
|
|
@@ -34,6 +34,22 @@ Use Speckit for Agile + TDD work.
|
|
|
34
34
|
content: json({
|
|
35
35
|
permissions: {
|
|
36
36
|
defaultMode: "ask",
|
|
37
|
+
deny: [
|
|
38
|
+
"Read(.env*)",
|
|
39
|
+
"Read(**/*.pem)",
|
|
40
|
+
"Read(**/*.key)",
|
|
41
|
+
"Read(**/id_rsa)",
|
|
42
|
+
"Read(**/id_ed25519)",
|
|
43
|
+
"Write(.env*)",
|
|
44
|
+
"Write(**/*.pem)",
|
|
45
|
+
"Write(**/*.key)",
|
|
46
|
+
"Bash(rm -rf*)",
|
|
47
|
+
"Bash(git reset --hard*)",
|
|
48
|
+
"Bash(git clean -fd*)",
|
|
49
|
+
"Bash(git push --force*)",
|
|
50
|
+
"Bash(sudo *)",
|
|
51
|
+
],
|
|
52
|
+
ask: ["Bash(git push*)", "Bash(npm publish*)", "Bash(* deploy*)"],
|
|
37
53
|
},
|
|
38
54
|
includeCoAuthoredBy: false,
|
|
39
55
|
}),
|
|
@@ -34,6 +34,8 @@ Rules:
|
|
|
34
34
|
content: text(`model = "gpt-5.4"
|
|
35
35
|
approval_policy = "on-request"
|
|
36
36
|
sandbox_mode = "workspace-write"
|
|
37
|
+
allowed_approval_policies = ["untrusted", "on-request"]
|
|
38
|
+
allowed_sandbox_modes = ["read-only", "workspace-write"]
|
|
37
39
|
|
|
38
40
|
[tools]
|
|
39
41
|
web_search = true
|
|
@@ -8,6 +8,7 @@ export const cursorAdapter = {
|
|
|
8
8
|
".cursor/rules/speckit-review.mdc",
|
|
9
9
|
".cursor/rules/speckit-enterprise-safety.mdc",
|
|
10
10
|
".cursor/mcp.json",
|
|
11
|
+
".cursor/cli.json",
|
|
11
12
|
"AGENTS.md",
|
|
12
13
|
],
|
|
13
14
|
render() {
|
|
@@ -22,6 +23,28 @@ export const cursorAdapter = {
|
|
|
22
23
|
mcpServers: {},
|
|
23
24
|
}),
|
|
24
25
|
},
|
|
26
|
+
{
|
|
27
|
+
path: ".cursor/cli.json",
|
|
28
|
+
content: json({
|
|
29
|
+
permissions: {
|
|
30
|
+
allow: ["Shell(git)", "Shell(npm)", "Shell(node)", "Read(.speckit/**)", "Read(src/**)", "Read(tests/**)"],
|
|
31
|
+
deny: [
|
|
32
|
+
"Read(.env*)",
|
|
33
|
+
"Read(**/*.pem)",
|
|
34
|
+
"Read(**/*.key)",
|
|
35
|
+
"Read(**/id_rsa)",
|
|
36
|
+
"Read(**/id_ed25519)",
|
|
37
|
+
"Write(.env*)",
|
|
38
|
+
"Write(**/*.pem)",
|
|
39
|
+
"Write(**/*.key)",
|
|
40
|
+
"Shell(rm)",
|
|
41
|
+
"Shell(sudo)",
|
|
42
|
+
"Shell(chmod)",
|
|
43
|
+
"Shell(chown)",
|
|
44
|
+
],
|
|
45
|
+
},
|
|
46
|
+
}),
|
|
47
|
+
},
|
|
25
48
|
{
|
|
26
49
|
path: "AGENTS.md",
|
|
27
50
|
content: markdown(`# Speckit Agent Instructions
|
package/dist/cli.js
CHANGED
|
@@ -6,9 +6,11 @@ import { contextCommand } from "./commands/context.js";
|
|
|
6
6
|
import { quickCommand } from "./commands/quick.js";
|
|
7
7
|
import { planCommand } from "./commands/plan.js";
|
|
8
8
|
import { memoryCommand } from "./commands/memory.js";
|
|
9
|
+
import { permissionsCommand } from "./commands/permissions.js";
|
|
9
10
|
import { sessionCommand } from "./commands/session.js";
|
|
10
11
|
import { sprintCommand } from "./commands/sprint.js";
|
|
11
12
|
import { graphCommand } from "./commands/graph.js";
|
|
13
|
+
import { validateCommand } from "./commands/validate.js";
|
|
12
14
|
import { triageCommand } from "./commands/triage.js";
|
|
13
15
|
import { nextCommand } from "./commands/next.js";
|
|
14
16
|
import { syncCommand } from "./commands/sync.js";
|
|
@@ -41,6 +43,13 @@ export async function main(argv = process.argv.slice(2), root = process.cwd()) {
|
|
|
41
43
|
return planCommand({ root, intent: requiredIntent(parsed) });
|
|
42
44
|
case "memory":
|
|
43
45
|
return memoryCommand({ root, action: requiredAction(parsed) });
|
|
46
|
+
case "permissions":
|
|
47
|
+
return permissionsCommand({
|
|
48
|
+
action: requiredAction(parsed),
|
|
49
|
+
path: value(parsed, "path"),
|
|
50
|
+
command: value(parsed, "command"),
|
|
51
|
+
json: has(parsed, "json"),
|
|
52
|
+
});
|
|
44
53
|
case "session":
|
|
45
54
|
return sessionCommand({
|
|
46
55
|
root,
|
|
@@ -53,6 +62,8 @@ export async function main(argv = process.argv.slice(2), root = process.cwd()) {
|
|
|
53
62
|
return sprintCommand({ root, action: requiredAction(parsed), json: has(parsed, "json") });
|
|
54
63
|
case "graph":
|
|
55
64
|
return graphCommand({ root, action: requiredAction(parsed), json: has(parsed, "json") });
|
|
65
|
+
case "validate":
|
|
66
|
+
return validateCommand({ root, json: has(parsed, "json") });
|
|
56
67
|
case "triage":
|
|
57
68
|
return triageCommand({ root, json: has(parsed, "json") });
|
|
58
69
|
case "next":
|
|
@@ -136,9 +147,11 @@ Usage:
|
|
|
136
147
|
speckit quick "<intent>"
|
|
137
148
|
speckit plan "<intent>"
|
|
138
149
|
speckit memory refresh
|
|
150
|
+
speckit permissions audit [--path <path>] [--command <command>] [--json]
|
|
139
151
|
speckit session start|checkpoint|compact|resume|status [target] [--note "..."] [--json]
|
|
140
152
|
speckit sprint plan|next [--json]
|
|
141
153
|
speckit graph triage|plan|insights [--json]
|
|
154
|
+
speckit validate [--json]
|
|
142
155
|
speckit triage [--json]
|
|
143
156
|
speckit next
|
|
144
157
|
speckit sync
|
package/dist/commands/doctor.js
CHANGED
|
@@ -5,11 +5,13 @@ import { checkTools } from "../adapters/tool-checks.js";
|
|
|
5
5
|
import { detectTestCommands } from "../core/test-detection.js";
|
|
6
6
|
import { coreFiles, enterpriseFiles } from "../core/scaffold.js";
|
|
7
7
|
import { agentFiles } from "../core/agent-scaffold.js";
|
|
8
|
+
import { validateWorkflowContract } from "../core/workflow-validator.js";
|
|
8
9
|
export async function doctorCommand(options) {
|
|
9
10
|
const stdout = options.stdout ?? console;
|
|
10
11
|
const tools = checkTools();
|
|
11
12
|
const tests = await detectTestCommands(options.root);
|
|
12
13
|
const deepChecks = options.deep ? await runDeepChecks(options.root) : [];
|
|
14
|
+
const contractChecks = options.deep ? await validateWorkflowContract(options.root) : [];
|
|
13
15
|
const adapters = await Promise.all(getAdapters("all").map(async (adapter) => ({
|
|
14
16
|
name: adapter.name,
|
|
15
17
|
...(await adapterStatus(options.root, adapter.outputPaths)),
|
|
@@ -19,8 +21,9 @@ export async function doctorCommand(options) {
|
|
|
19
21
|
tools,
|
|
20
22
|
tests,
|
|
21
23
|
deepChecks,
|
|
24
|
+
contractChecks,
|
|
22
25
|
adapters,
|
|
23
|
-
status: statusFor(tools, deepChecks),
|
|
26
|
+
status: statusFor(tools, deepChecks, contractChecks),
|
|
24
27
|
};
|
|
25
28
|
if (options.json) {
|
|
26
29
|
stdout.log(JSON.stringify(report, null, 2));
|
|
@@ -33,6 +36,9 @@ export async function doctorCommand(options) {
|
|
|
33
36
|
for (const check of deepChecks) {
|
|
34
37
|
stdout.log(`Deep ${check.name}: ${check.ok ? "ok" : "missing"}`);
|
|
35
38
|
}
|
|
39
|
+
for (const check of contractChecks) {
|
|
40
|
+
stdout.log(`Contract ${check.name}: ${check.ok ? "ok" : check.detail}`);
|
|
41
|
+
}
|
|
36
42
|
for (const adapter of adapters) {
|
|
37
43
|
stdout.log(`Adapter ${adapter.name}: ${adapter.present}/${adapter.total} files present`);
|
|
38
44
|
}
|
|
@@ -56,10 +62,11 @@ async function fileExists(root, path) {
|
|
|
56
62
|
return false;
|
|
57
63
|
}
|
|
58
64
|
}
|
|
59
|
-
function statusFor(tools, deepChecks) {
|
|
65
|
+
function statusFor(tools, deepChecks, contractChecks) {
|
|
60
66
|
const requiredToolsMissing = tools.some((tool) => tool.required && !tool.available);
|
|
61
67
|
const deepChecksMissing = deepChecks.some((check) => !check.ok);
|
|
62
|
-
|
|
68
|
+
const contractChecksFailed = contractChecks.some((check) => !check.ok);
|
|
69
|
+
return requiredToolsMissing || deepChecksMissing || contractChecksFailed ? "needs-attention" : "ok";
|
|
63
70
|
}
|
|
64
71
|
async function adapterStatus(root, paths) {
|
|
65
72
|
const missing = [];
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { auditPermission } from "../core/permission-auditor.js";
|
|
2
|
+
export async function permissionsCommand(options) {
|
|
3
|
+
const stdout = options.stdout ?? console;
|
|
4
|
+
if (options.action !== "audit") {
|
|
5
|
+
stdout.error?.(`Unknown permissions action: ${options.action}`);
|
|
6
|
+
return 1;
|
|
7
|
+
}
|
|
8
|
+
const result = auditPermission({ path: options.path, command: options.command });
|
|
9
|
+
if (options.json) {
|
|
10
|
+
stdout.log(JSON.stringify(result, null, 2));
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
stdout.log(`Speckit permission audit: ${result.status}`);
|
|
14
|
+
for (const reason of result.reasons)
|
|
15
|
+
stdout.log(`- ${reason}`);
|
|
16
|
+
}
|
|
17
|
+
return result.status === "deny" ? 1 : 0;
|
|
18
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { validateWorkflowContract } from "../core/workflow-validator.js";
|
|
2
|
+
export async function validateCommand(options) {
|
|
3
|
+
const stdout = options.stdout ?? console;
|
|
4
|
+
const checks = await validateWorkflowContract(options.root);
|
|
5
|
+
const status = checks.every((check) => check.ok) ? "ok" : "needs-attention";
|
|
6
|
+
const report = { status, checks };
|
|
7
|
+
if (options.json) {
|
|
8
|
+
stdout.log(JSON.stringify(report, null, 2));
|
|
9
|
+
}
|
|
10
|
+
else {
|
|
11
|
+
stdout.log(`Speckit workflow contract: ${status}`);
|
|
12
|
+
for (const check of checks) {
|
|
13
|
+
stdout.log(`${check.ok ? "ok" : "fail"} ${check.name}: ${check.detail}`);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return status === "ok" ? 0 : 1;
|
|
17
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type PermissionAuditInput = {
|
|
2
|
+
path?: string;
|
|
3
|
+
command?: string;
|
|
4
|
+
};
|
|
5
|
+
export type PermissionAuditResult = {
|
|
6
|
+
status: "allow" | "ask" | "deny";
|
|
7
|
+
reasons: string[];
|
|
8
|
+
};
|
|
9
|
+
export declare function auditPermission(input: PermissionAuditInput): PermissionAuditResult;
|
|
10
|
+
export declare function validatePermissionPolicy(root: string): Promise<string[]>;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { basename, join } from "node:path";
|
|
3
|
+
import { destructiveCommands, heavyPatterns, releaseCommands, sensitivePatterns } from "./permission-policy.js";
|
|
4
|
+
const safeSecretSuffixes = [".example", ".sample", ".template"];
|
|
5
|
+
export function auditPermission(input) {
|
|
6
|
+
const reasons = [];
|
|
7
|
+
if (input.path) {
|
|
8
|
+
if (isSensitivePath(input.path))
|
|
9
|
+
reasons.push(`privacy:${input.path}`);
|
|
10
|
+
if (isHeavyPath(input.path))
|
|
11
|
+
reasons.push(`scout:${input.path}`);
|
|
12
|
+
}
|
|
13
|
+
if (input.command) {
|
|
14
|
+
if (matchesAny(input.command, destructiveCommands))
|
|
15
|
+
reasons.push(`destructive:${input.command}`);
|
|
16
|
+
if (matchesAny(input.command, releaseCommands)) {
|
|
17
|
+
return { status: reasons.length > 0 ? "deny" : "ask", reasons: [`release:${input.command}`, ...reasons] };
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return reasons.length > 0 ? { status: "deny", reasons } : { status: "allow", reasons };
|
|
21
|
+
}
|
|
22
|
+
export async function validatePermissionPolicy(root) {
|
|
23
|
+
const content = await read(join(root, ".speckit/permissions.yaml"));
|
|
24
|
+
const missing = [];
|
|
25
|
+
if (!content)
|
|
26
|
+
return ["missing .speckit/permissions.yaml"];
|
|
27
|
+
for (const token of ["precedence:", "privacy:", "scout:", "destructive:", "release:", "file_write: ask"]) {
|
|
28
|
+
if (!content.includes(token))
|
|
29
|
+
missing.push(token);
|
|
30
|
+
}
|
|
31
|
+
for (const pattern of [".env", "node_modules", "git reset --hard*", "npm publish*"]) {
|
|
32
|
+
if (!content.includes(pattern))
|
|
33
|
+
missing.push(pattern);
|
|
34
|
+
}
|
|
35
|
+
return missing;
|
|
36
|
+
}
|
|
37
|
+
function isSensitivePath(path) {
|
|
38
|
+
const clean = normalize(path);
|
|
39
|
+
const name = basename(clean);
|
|
40
|
+
if (safeSecretSuffixes.some((suffix) => name.endsWith(suffix)))
|
|
41
|
+
return false;
|
|
42
|
+
return sensitivePatterns.some((pattern) => matchGlob(clean, pattern));
|
|
43
|
+
}
|
|
44
|
+
function isHeavyPath(path) {
|
|
45
|
+
const parts = normalize(path).split("/");
|
|
46
|
+
return heavyPatterns.some((pattern) => parts.includes(pattern.replace(/"/g, "")));
|
|
47
|
+
}
|
|
48
|
+
function matchesAny(command, patterns) {
|
|
49
|
+
return patterns.some((pattern) => matchGlob(command.trim(), pattern));
|
|
50
|
+
}
|
|
51
|
+
function matchGlob(value, pattern) {
|
|
52
|
+
const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, ".*").replace(/\*/g, ".*");
|
|
53
|
+
return new RegExp(`^${escaped}$`).test(value) || new RegExp(escaped).test(value);
|
|
54
|
+
}
|
|
55
|
+
function normalize(value) {
|
|
56
|
+
return value.replace(/\\/g, "/");
|
|
57
|
+
}
|
|
58
|
+
async function read(path) {
|
|
59
|
+
try {
|
|
60
|
+
return await readFile(path, "utf8");
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { ManagedFile } from "./managed-files.js";
|
|
2
|
+
export declare const sensitivePatterns: string[];
|
|
3
|
+
export declare const heavyPatterns: string[];
|
|
4
|
+
export declare const destructiveCommands: string[];
|
|
5
|
+
export declare const releaseCommands: string[];
|
|
6
|
+
export declare function permissionPolicyFile(): ManagedFile;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { text } from "./managed-files.js";
|
|
2
|
+
export const sensitivePatterns = [
|
|
3
|
+
".env",
|
|
4
|
+
".env.*",
|
|
5
|
+
"**/.env",
|
|
6
|
+
"**/.env.*",
|
|
7
|
+
"**/credentials*",
|
|
8
|
+
"**/secret.yml",
|
|
9
|
+
"**/secrets.yml",
|
|
10
|
+
"**/secret.yaml",
|
|
11
|
+
"**/secrets.yaml",
|
|
12
|
+
"**/*.pem",
|
|
13
|
+
"**/*.key",
|
|
14
|
+
"**/id_rsa",
|
|
15
|
+
"**/id_ed25519",
|
|
16
|
+
];
|
|
17
|
+
export const heavyPatterns = [
|
|
18
|
+
"node_modules",
|
|
19
|
+
"dist",
|
|
20
|
+
"build",
|
|
21
|
+
".next",
|
|
22
|
+
".nuxt",
|
|
23
|
+
"__pycache__",
|
|
24
|
+
".venv",
|
|
25
|
+
"venv",
|
|
26
|
+
"vendor",
|
|
27
|
+
"target",
|
|
28
|
+
".git",
|
|
29
|
+
"coverage",
|
|
30
|
+
];
|
|
31
|
+
export const destructiveCommands = [
|
|
32
|
+
"rm -rf*",
|
|
33
|
+
"git reset --hard*",
|
|
34
|
+
"git clean -fd*",
|
|
35
|
+
"git push --force*",
|
|
36
|
+
"sudo *",
|
|
37
|
+
"chmod -R*",
|
|
38
|
+
"chown -R*",
|
|
39
|
+
];
|
|
40
|
+
export const releaseCommands = ["git push*", "npm publish*", "* deploy*", "vercel --prod*", "fly deploy*"];
|
|
41
|
+
export function permissionPolicyFile() {
|
|
42
|
+
return {
|
|
43
|
+
path: ".speckit/permissions.yaml",
|
|
44
|
+
content: text(`version: 1
|
|
45
|
+
mode: enterprise
|
|
46
|
+
precedence:
|
|
47
|
+
- deny
|
|
48
|
+
- ask
|
|
49
|
+
- allow
|
|
50
|
+
defaults:
|
|
51
|
+
file_read: allow_workspace
|
|
52
|
+
file_write: ask
|
|
53
|
+
shell: ask
|
|
54
|
+
network: ask
|
|
55
|
+
external_directory: deny
|
|
56
|
+
guards:
|
|
57
|
+
privacy:
|
|
58
|
+
action: deny
|
|
59
|
+
allow_examples: true
|
|
60
|
+
patterns:
|
|
61
|
+
${sensitivePatterns.map((pattern) => ` - "${pattern}"`).join("\n")}
|
|
62
|
+
scout:
|
|
63
|
+
action: deny
|
|
64
|
+
patterns:
|
|
65
|
+
${heavyPatterns.map((pattern) => ` - "${pattern}"`).join("\n")}
|
|
66
|
+
destructive:
|
|
67
|
+
action: deny
|
|
68
|
+
commands:
|
|
69
|
+
${destructiveCommands.map((command) => ` - "${command}"`).join("\n")}
|
|
70
|
+
release:
|
|
71
|
+
action: ask
|
|
72
|
+
commands:
|
|
73
|
+
${releaseCommands.map((command) => ` - "${command}"`).join("\n")}
|
|
74
|
+
phases:
|
|
75
|
+
shape:
|
|
76
|
+
file_write:
|
|
77
|
+
- ".speckit/specs/**"
|
|
78
|
+
plan:
|
|
79
|
+
file_write:
|
|
80
|
+
- ".speckit/plans/**"
|
|
81
|
+
- ".speckit/stories/**"
|
|
82
|
+
- ".speckit/evidence/**"
|
|
83
|
+
review:
|
|
84
|
+
file_write: deny
|
|
85
|
+
shell_allow:
|
|
86
|
+
- "git diff*"
|
|
87
|
+
- "git log*"
|
|
88
|
+
- "npm test*"
|
|
89
|
+
ship:
|
|
90
|
+
shell_ask:
|
|
91
|
+
${releaseCommands.map((command) => ` - "${command}"`).join("\n")}`),
|
|
92
|
+
};
|
|
93
|
+
}
|
package/dist/core/scaffold.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { markdown, text } from "./managed-files.js";
|
|
2
2
|
import { agilePolicy, enterpriseSafetyPolicy, tddPolicy, workflowReview, workflowShape, workflowTddRun, } from "./policy.js";
|
|
3
|
+
import { permissionPolicyFile } from "./permission-policy.js";
|
|
3
4
|
import { storyTemplate, tddEvidenceTemplate } from "./templates.js";
|
|
4
5
|
export function coreFiles() {
|
|
5
6
|
return [
|
|
@@ -31,6 +32,7 @@ adapters:
|
|
|
31
32
|
path: ".speckit/rules/enterprise-safety.md",
|
|
32
33
|
content: markdown(enterpriseSafetyPolicy),
|
|
33
34
|
},
|
|
35
|
+
permissionPolicyFile(),
|
|
34
36
|
{ path: ".speckit/workflows/shape.md", content: markdown(workflowShape) },
|
|
35
37
|
{ path: ".speckit/workflows/tdd-run.md", content: markdown(workflowTddRun) },
|
|
36
38
|
{ path: ".speckit/workflows/review.md", content: markdown(workflowReview) },
|
|
@@ -87,6 +87,9 @@ function skill(name, phase, purpose, practices) {
|
|
|
87
87
|
export function specSkillFiles() {
|
|
88
88
|
return [catalogFile(), schemaFile(), ...skills.map(skillFile)];
|
|
89
89
|
}
|
|
90
|
+
export function specSkillNames() {
|
|
91
|
+
return skills.map((skill) => skill.name);
|
|
92
|
+
}
|
|
90
93
|
function catalogFile() {
|
|
91
94
|
return {
|
|
92
95
|
path: ".speckit/skills/catalog.md",
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const workflowContract: {
|
|
2
|
+
readonly phases: readonly ["start", "memory", "sprint", "context", "sync", "triage", "ready", "run", "checkpoint", "review", "close"];
|
|
3
|
+
readonly skills: string[];
|
|
4
|
+
readonly statuses: readonly ["DONE", "DONE_WITH_CONCERNS", "BLOCKED", "NEEDS_CONTEXT"];
|
|
5
|
+
readonly requiredPromptTerms: readonly [readonly ["project memory", ".speckit/memory/project-context.md"], readonly ["active session", ".speckit/sessions/active.md"], readonly ["current context", ".speckit/context/current.md"], readonly ["subagent handoff", ".speckit/context/subagent-handoff.md"], readonly ["acceptance criteria", "AC coverage", "ACs"], readonly ["red-green-refactor", "red/green/refactor"], readonly ["checkpoint", "speckit session checkpoint"], readonly ["compact", "compaction", "speckit session compact"], readonly ["robot-safe", "bv --robot", "robot commands"], readonly ["ready-for-dev", "speckit ready"]];
|
|
6
|
+
readonly requiredRouterTerms: readonly ["smallest matching Speckit skill", "Work context path", "Reports path", "Plans path", "Hydrate runtime tasks", "Sync completed runtime tasks"];
|
|
7
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { specSkillNames } from "./skill-catalog.js";
|
|
2
|
+
export const workflowContract = {
|
|
3
|
+
phases: [
|
|
4
|
+
"start",
|
|
5
|
+
"memory",
|
|
6
|
+
"sprint",
|
|
7
|
+
"context",
|
|
8
|
+
"sync",
|
|
9
|
+
"triage",
|
|
10
|
+
"ready",
|
|
11
|
+
"run",
|
|
12
|
+
"checkpoint",
|
|
13
|
+
"review",
|
|
14
|
+
"close",
|
|
15
|
+
],
|
|
16
|
+
skills: specSkillNames(),
|
|
17
|
+
statuses: ["DONE", "DONE_WITH_CONCERNS", "BLOCKED", "NEEDS_CONTEXT"],
|
|
18
|
+
requiredPromptTerms: [
|
|
19
|
+
["project memory", ".speckit/memory/project-context.md"],
|
|
20
|
+
["active session", ".speckit/sessions/active.md"],
|
|
21
|
+
["current context", ".speckit/context/current.md"],
|
|
22
|
+
["subagent handoff", ".speckit/context/subagent-handoff.md"],
|
|
23
|
+
["acceptance criteria", "AC coverage", "ACs"],
|
|
24
|
+
["red-green-refactor", "red/green/refactor"],
|
|
25
|
+
["checkpoint", "speckit session checkpoint"],
|
|
26
|
+
["compact", "compaction", "speckit session compact"],
|
|
27
|
+
["robot-safe", "bv --robot", "robot commands"],
|
|
28
|
+
["ready-for-dev", "speckit ready"],
|
|
29
|
+
],
|
|
30
|
+
requiredRouterTerms: [
|
|
31
|
+
"smallest matching Speckit skill",
|
|
32
|
+
"Work context path",
|
|
33
|
+
"Reports path",
|
|
34
|
+
"Plans path",
|
|
35
|
+
"Hydrate runtime tasks",
|
|
36
|
+
"Sync completed runtime tasks",
|
|
37
|
+
],
|
|
38
|
+
};
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { access, readFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { getAdapters } from "../config/adapter-registry.js";
|
|
4
|
+
import { validatePermissionPolicy } from "./permission-auditor.js";
|
|
5
|
+
import { workflowContract } from "./workflow-contract.js";
|
|
6
|
+
export async function validateWorkflowContract(root) {
|
|
7
|
+
const checks = [];
|
|
8
|
+
const catalog = await read(root, ".speckit/skills/catalog.md");
|
|
9
|
+
const router = await read(root, ".speckit/agents/super-agent.md");
|
|
10
|
+
const flow = await read(root, ".speckit/flows/spec-flow.md");
|
|
11
|
+
const runPrompt = await read(root, ".speckit/prompts/spec-run.md");
|
|
12
|
+
checks.push(requiredFile(".speckit/skills/catalog.md", catalog));
|
|
13
|
+
checks.push(requiredFile(".speckit/agents/super-agent.md", router));
|
|
14
|
+
checks.push(requiredFile(".speckit/flows/spec-flow.md", flow));
|
|
15
|
+
checks.push(requiredFile(".speckit/prompts/spec-run.md", runPrompt));
|
|
16
|
+
checks.push(await skillsCheck(root, catalog));
|
|
17
|
+
checks.push(containsAll("super-agent router", router, workflowContract.requiredRouterTerms));
|
|
18
|
+
checks.push(orderedFlowCheck(flow));
|
|
19
|
+
checks.push(containsAll("enterprise run prompt", runPrompt, workflowContract.requiredPromptTerms));
|
|
20
|
+
checks.push(await permissionPolicyCheck(root));
|
|
21
|
+
checks.push(await adapterPromptCheck(root));
|
|
22
|
+
return checks;
|
|
23
|
+
}
|
|
24
|
+
async function permissionPolicyCheck(root) {
|
|
25
|
+
const missing = await validatePermissionPolicy(root);
|
|
26
|
+
return {
|
|
27
|
+
name: "permission policy",
|
|
28
|
+
ok: missing.length === 0,
|
|
29
|
+
detail: missing.length === 0 ? "aligned" : `missing: ${missing.join(", ")}`,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function requiredFile(path, content) {
|
|
33
|
+
return {
|
|
34
|
+
name: path,
|
|
35
|
+
ok: content !== undefined,
|
|
36
|
+
detail: content === undefined ? "missing" : "present",
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
async function skillsCheck(root, catalog) {
|
|
40
|
+
const missing = [];
|
|
41
|
+
for (const skill of workflowContract.skills) {
|
|
42
|
+
if (!(await exists(root, `.speckit/skills/${skill}.md`)) || !catalog?.includes(skill)) {
|
|
43
|
+
missing.push(skill);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
name: "core skills",
|
|
48
|
+
ok: missing.length === 0,
|
|
49
|
+
detail: missing.length === 0 ? `${workflowContract.skills.length} skills aligned` : `missing: ${missing.join(", ")}`,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function orderedFlowCheck(flow) {
|
|
53
|
+
if (!flow)
|
|
54
|
+
return { name: "workflow phase order", ok: false, detail: "flow file missing" };
|
|
55
|
+
let cursor = -1;
|
|
56
|
+
const missing = [];
|
|
57
|
+
for (const phase of workflowContract.phases) {
|
|
58
|
+
const next = flow.indexOf(phase, cursor + 1);
|
|
59
|
+
if (next === -1) {
|
|
60
|
+
missing.push(phase);
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
cursor = next;
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
name: "workflow phase order",
|
|
67
|
+
ok: missing.length === 0,
|
|
68
|
+
detail: missing.length === 0 ? workflowContract.phases.join(" -> ") : `missing or out of order: ${missing.join(", ")}`,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function containsAll(name, content, terms) {
|
|
72
|
+
const haystack = content?.toLowerCase() ?? "";
|
|
73
|
+
const missing = terms.filter((term) => !matchesTerm(haystack, term)).map(termLabel);
|
|
74
|
+
return {
|
|
75
|
+
name,
|
|
76
|
+
ok: missing.length === 0,
|
|
77
|
+
detail: missing.length === 0 ? "aligned" : `missing: ${missing.join(", ")}`,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function matchesTerm(haystack, term) {
|
|
81
|
+
const alternatives = Array.isArray(term) ? term : [term];
|
|
82
|
+
return alternatives.some((alternative) => haystack.includes(alternative.toLowerCase()));
|
|
83
|
+
}
|
|
84
|
+
function termLabel(term) {
|
|
85
|
+
return typeof term === "string" ? term : term[0];
|
|
86
|
+
}
|
|
87
|
+
async function adapterPromptCheck(root) {
|
|
88
|
+
const missing = [];
|
|
89
|
+
const checked = [];
|
|
90
|
+
for (const adapter of getAdapters("all")) {
|
|
91
|
+
const content = await readExisting(root, adapter.outputPaths);
|
|
92
|
+
if (!content)
|
|
93
|
+
continue;
|
|
94
|
+
checked.push(adapter.name);
|
|
95
|
+
const check = containsAll(adapter.name, content, workflowContract.requiredPromptTerms);
|
|
96
|
+
if (!check.ok)
|
|
97
|
+
missing.push(`${adapter.name} (${check.detail})`);
|
|
98
|
+
}
|
|
99
|
+
if (checked.length === 0) {
|
|
100
|
+
return { name: "adapter prompt contracts", ok: false, detail: "no adapter files found" };
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
name: "adapter prompt contracts",
|
|
104
|
+
ok: missing.length === 0,
|
|
105
|
+
detail: missing.length === 0 ? `aligned: ${checked.join(", ")}` : missing.join("; "),
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
async function readExisting(root, paths) {
|
|
109
|
+
const chunks = [];
|
|
110
|
+
for (const path of paths) {
|
|
111
|
+
const content = await read(root, path);
|
|
112
|
+
if (content)
|
|
113
|
+
chunks.push(content);
|
|
114
|
+
}
|
|
115
|
+
return chunks.length > 0 ? chunks.join("\n") : undefined;
|
|
116
|
+
}
|
|
117
|
+
async function read(root, path) {
|
|
118
|
+
try {
|
|
119
|
+
return await readFile(join(root, path), "utf8");
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
return undefined;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
async function exists(root, path) {
|
|
126
|
+
try {
|
|
127
|
+
await access(join(root, path));
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
Speckit MVP is implemented and pushed to `git@github.com:trieungoctam/speckit.git` on `main`.
|
|
6
6
|
|
|
7
|
-
Current package target: `@trieungoctam/speckit@0.3.
|
|
7
|
+
Current package target: `@trieungoctam/speckit@0.3.1`.
|
|
8
8
|
|
|
9
9
|
The CLI is npx-ready, generates Agile + TDD rules, creates workflow artifacts, supports five IDE adapters, wraps Beads Viewer safely, includes an enterprise harness, and has automated prompt/readiness/session tests plus CI.
|
|
10
10
|
|
|
@@ -21,6 +21,8 @@ The CLI is npx-ready, generates Agile + TDD rules, creates workflow artifacts, s
|
|
|
21
21
|
| Sprint automation | MVP Complete | `sprint plan` and `sprint next` select work from synced stories. |
|
|
22
22
|
| Enterprise harness | MVP Complete | `init --enterprise` creates flow, tool-policy, and prompt harness files; `doctor --deep` verifies them. |
|
|
23
23
|
| Curated skill catalog | Complete | Speckit now generates focused phase skills, a schema, delegation statuses, and task hydration/sync-back guidance. |
|
|
24
|
+
| Workflow contract validator | Complete | `speckit validate` and `doctor --deep` check phase order, core skills, router terms, run prompt terms, and adapter parity. |
|
|
25
|
+
| Permission policy | MVP Complete | `.speckit/permissions.yaml` and `permissions audit` cover privacy files, heavy paths, destructive commands, and release commands. |
|
|
24
26
|
| Validation | Complete | Build and `node:test` suite pass locally with flow contract gates. |
|
|
25
27
|
| GitHub publish | Complete | Initial code pushed to `trieungoctam/speckit` at commit `7e5c582`; status docs follow-up in progress. |
|
|
26
28
|
| npm package readiness | Ready | Package scoped as `@trieungoctam/speckit`; `npm pack --dry-run` passes. |
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
# Permission Rules Research
|
|
2
|
+
|
|
3
|
+
Date: 2026-05-10
|
|
4
|
+
|
|
5
|
+
## Scope
|
|
6
|
+
|
|
7
|
+
Research goal: define a permission-rule model for Speckit that can compile safely across Claude Code, Codex, Cursor, OpenCode, and Antigravity.
|
|
8
|
+
|
|
9
|
+
This research focuses on:
|
|
10
|
+
|
|
11
|
+
- File read/write permissions
|
|
12
|
+
- Shell command permissions
|
|
13
|
+
- Network access
|
|
14
|
+
- Approval modes
|
|
15
|
+
- Phase-specific workflow permissions
|
|
16
|
+
- Enterprise guardrails for secrets, destructive commands, and production access
|
|
17
|
+
|
|
18
|
+
## Key Findings
|
|
19
|
+
|
|
20
|
+
### 1. Permission Rules Must Be Deny-First
|
|
21
|
+
|
|
22
|
+
Claude Code documents allow, ask, and deny rules, with evaluation order `deny -> ask -> allow`; deny rules always take precedence. Cursor CLI also documents deny rules taking precedence over allow rules.
|
|
23
|
+
|
|
24
|
+
Speckit should use the same mental model across all adapters:
|
|
25
|
+
|
|
26
|
+
```text
|
|
27
|
+
deny > ask > allow
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
This should be a product invariant, even when a specific IDE has a different internal matching implementation.
|
|
31
|
+
|
|
32
|
+
Sources:
|
|
33
|
+
|
|
34
|
+
- [Claude Code permissions](https://code.claude.com/docs/en/permissions)
|
|
35
|
+
- [Cursor permissions](https://docs.cursor.com/cli/reference/permissions)
|
|
36
|
+
|
|
37
|
+
### 2. Permissions And Sandboxing Are Different Layers
|
|
38
|
+
|
|
39
|
+
Claude Code separates permissions from sandboxing: permissions control tool/file/domain access, while sandboxing enforces OS-level limits mainly around shell execution. OpenAI describes the same split for Codex: sandboxing defines execution boundaries, while approval policy decides when the agent must ask.
|
|
40
|
+
|
|
41
|
+
Speckit should model both layers:
|
|
42
|
+
|
|
43
|
+
- Permission policy: what the agent may attempt.
|
|
44
|
+
- Sandbox/approval policy: what the runtime may execute without approval.
|
|
45
|
+
|
|
46
|
+
Sources:
|
|
47
|
+
|
|
48
|
+
- [Claude Code permissions and sandboxing](https://code.claude.com/docs/en/permissions)
|
|
49
|
+
- [Running Codex safely at OpenAI](https://openai.com/index/running-codex-safely/)
|
|
50
|
+
- [Codex agent approvals and security](https://developers.openai.com/codex/agent-approvals-security)
|
|
51
|
+
|
|
52
|
+
### 3. Avoid Full Bypass Modes Except In Disposable Environments
|
|
53
|
+
|
|
54
|
+
Claude Code warns that bypass mode skips permission prompts and should only be used in isolated environments such as containers or VMs. Codex similarly labels no-sandbox/no-approval mode as dangerous and not recommended.
|
|
55
|
+
|
|
56
|
+
Speckit should never generate full-bypass configs by default.
|
|
57
|
+
|
|
58
|
+
Recommended default:
|
|
59
|
+
|
|
60
|
+
```text
|
|
61
|
+
approval: ask/on-request
|
|
62
|
+
sandbox: workspace-write
|
|
63
|
+
network: ask or denied by default
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Sources:
|
|
67
|
+
|
|
68
|
+
- [Claude Code permission modes](https://code.claude.com/docs/en/permissions)
|
|
69
|
+
- [Codex common sandbox and approval combinations](https://developers.openai.com/codex/agent-approvals-security)
|
|
70
|
+
|
|
71
|
+
### 4. Phase-Scoped Permissions Fit Speckit Best
|
|
72
|
+
|
|
73
|
+
Speckit already has phases: shape, research, plan, context, graph, session, TDD, test, debug, review, docs, ship.
|
|
74
|
+
|
|
75
|
+
Permission should follow phase, not only IDE:
|
|
76
|
+
|
|
77
|
+
| Phase | File Writes | Shell | Network | Notes |
|
|
78
|
+
| --- | --- | --- | --- | --- |
|
|
79
|
+
| shape | `.speckit/specs/**` only | ask | ask | No source edits |
|
|
80
|
+
| research | reports/docs only | ask | allow/ask | Sources required |
|
|
81
|
+
| plan | `.speckit/plans/**`, stories, evidence | ask | ask | No source edits |
|
|
82
|
+
| context | `.speckit/context/**` only | ask | deny/ask | Build handoff only |
|
|
83
|
+
| graph | `.speckit/sync/**`, `.beads/**` | allow Speckit wrappers | deny/ask | Robot-safe graph only |
|
|
84
|
+
| session | `.speckit/sessions/**`, memory | ask | deny | Durable state only |
|
|
85
|
+
| TDD | workspace source + tests | ask | ask | Requires ready gate |
|
|
86
|
+
| test | test artifacts only | allow test/build commands | deny/ask | No production |
|
|
87
|
+
| debug | source + tests after root cause | ask | ask | Capture failure first |
|
|
88
|
+
| review | no edits by default | allow `git diff`, `git log`, tests | deny | Read-only review |
|
|
89
|
+
| docs | docs + Speckit artifacts | ask | deny/ask | No source edits unless requested |
|
|
90
|
+
| ship | package/release files | ask | ask | Push/publish/deploy require explicit approval |
|
|
91
|
+
|
|
92
|
+
### 5. Adapter Mapping
|
|
93
|
+
|
|
94
|
+
#### Claude Code
|
|
95
|
+
|
|
96
|
+
Use `.claude/settings.json`:
|
|
97
|
+
|
|
98
|
+
- `permissions.defaultMode = "ask"` or conservative equivalent
|
|
99
|
+
- explicit deny rules for secrets and destructive commands
|
|
100
|
+
- ask rules for shell/network/publish/deploy
|
|
101
|
+
- optional hooks for runtime validation
|
|
102
|
+
- managed settings for enterprise to disable bypass/auto modes
|
|
103
|
+
|
|
104
|
+
Relevant details:
|
|
105
|
+
|
|
106
|
+
- Permission rule syntax supports `Tool` and `Tool(specifier)`.
|
|
107
|
+
- Bash rules support wildcards.
|
|
108
|
+
- Hooks can deny or force prompts before permission rules finish.
|
|
109
|
+
- Managed settings can prevent bypass mode and restrict user/project rules.
|
|
110
|
+
|
|
111
|
+
Source: [Claude Code permissions](https://code.claude.com/docs/en/permissions)
|
|
112
|
+
|
|
113
|
+
#### Codex
|
|
114
|
+
|
|
115
|
+
Use `.codex/config.toml`:
|
|
116
|
+
|
|
117
|
+
```toml
|
|
118
|
+
approval_policy = "on-request"
|
|
119
|
+
sandbox_mode = "workspace-write"
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Enterprise hardening can use requirements:
|
|
123
|
+
|
|
124
|
+
```toml
|
|
125
|
+
allowed_approval_policies = ["untrusted", "on-request"]
|
|
126
|
+
allowed_sandbox_modes = ["read-only", "workspace-write"]
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Avoid `danger-full-access` and no-approval modes for normal projects.
|
|
130
|
+
|
|
131
|
+
Sources:
|
|
132
|
+
|
|
133
|
+
- [Codex config reference](https://developers.openai.com/codex/config-reference)
|
|
134
|
+
- [Codex managed configuration](https://developers.openai.com/codex/enterprise/managed-configuration)
|
|
135
|
+
- [Codex agent approvals and security](https://developers.openai.com/codex/agent-approvals-security)
|
|
136
|
+
|
|
137
|
+
#### Cursor
|
|
138
|
+
|
|
139
|
+
Use project-level `.cursor/cli.json` for CLI permissions:
|
|
140
|
+
|
|
141
|
+
```json
|
|
142
|
+
{
|
|
143
|
+
"permissions": {
|
|
144
|
+
"allow": ["Shell(git)", "Shell(npm)", "Read(src/**/*.ts)"],
|
|
145
|
+
"deny": ["Shell(rm)", "Read(.env*)", "Write(**/*.key)", "Write(**/.env*)"]
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Cursor permission tokens cover:
|
|
151
|
+
|
|
152
|
+
- `Shell(commandBase)`
|
|
153
|
+
- `Read(pathOrGlob)`
|
|
154
|
+
- `Write(pathOrGlob)`
|
|
155
|
+
|
|
156
|
+
Source: [Cursor permissions](https://docs.cursor.com/cli/reference/permissions)
|
|
157
|
+
|
|
158
|
+
#### OpenCode
|
|
159
|
+
|
|
160
|
+
Use `permission` in `opencode.json` and Markdown agents.
|
|
161
|
+
|
|
162
|
+
OpenCode supports:
|
|
163
|
+
|
|
164
|
+
- `ask`
|
|
165
|
+
- `allow`
|
|
166
|
+
- `deny`
|
|
167
|
+
- per-agent override
|
|
168
|
+
- specific bash command patterns
|
|
169
|
+
- task permission controls
|
|
170
|
+
|
|
171
|
+
For Speckit, keep planner/reviewer read-only, TDD developer ask-gated, and ship commands ask-gated.
|
|
172
|
+
|
|
173
|
+
Source: [OpenCode agents and permissions](https://opencode.ai/docs/agents/)
|
|
174
|
+
|
|
175
|
+
#### Antigravity
|
|
176
|
+
|
|
177
|
+
The accessible public docs emphasize configurable autonomy, artifact review, and approval for critical operations, but the browsable official pages did not expose a stable machine-readable permission schema during this research.
|
|
178
|
+
|
|
179
|
+
Speckit should treat Antigravity permissions as instruction-level until a stable official config format is confirmed:
|
|
180
|
+
|
|
181
|
+
- generate `.agents/rules/speckit-enterprise-safety.md`
|
|
182
|
+
- require human approval for destructive commands, production changes, secrets access, deployment
|
|
183
|
+
- require artifact review before close
|
|
184
|
+
- avoid claiming hard enforcement unless adapter support is verified
|
|
185
|
+
|
|
186
|
+
Source:
|
|
187
|
+
|
|
188
|
+
- [Antigravity agents overview](https://antigravity.im/agents) — independent guide, not official Google documentation
|
|
189
|
+
|
|
190
|
+
## Recommended Speckit Permission Model
|
|
191
|
+
|
|
192
|
+
Speckit should own a portable policy and compile it per IDE:
|
|
193
|
+
|
|
194
|
+
```yaml
|
|
195
|
+
version: 1
|
|
196
|
+
default:
|
|
197
|
+
file_read: allow_workspace
|
|
198
|
+
file_write: ask
|
|
199
|
+
shell: ask
|
|
200
|
+
network: ask
|
|
201
|
+
external_directory: deny
|
|
202
|
+
secrets: deny
|
|
203
|
+
destructive: deny
|
|
204
|
+
production: ask
|
|
205
|
+
phases:
|
|
206
|
+
review:
|
|
207
|
+
file_write: deny
|
|
208
|
+
shell_allow:
|
|
209
|
+
- git diff*
|
|
210
|
+
- git log*
|
|
211
|
+
- npm test*
|
|
212
|
+
ship:
|
|
213
|
+
shell_ask:
|
|
214
|
+
- git push*
|
|
215
|
+
- npm publish*
|
|
216
|
+
- "* deploy*"
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## Commands To Add
|
|
220
|
+
|
|
221
|
+
Recommended CLI surface:
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
speckit permissions init
|
|
225
|
+
speckit permissions audit --json
|
|
226
|
+
speckit permissions compile --ide all
|
|
227
|
+
speckit validate --permissions --json
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Minimum useful implementation:
|
|
231
|
+
|
|
232
|
+
1. Generate `.speckit/permissions.yaml`.
|
|
233
|
+
2. Add permission checks to `speckit validate`.
|
|
234
|
+
3. Compile:
|
|
235
|
+
- Claude Code: `.claude/settings.json`
|
|
236
|
+
- Codex: `.codex/config.toml`
|
|
237
|
+
- Cursor: `.cursor/cli.json`
|
|
238
|
+
- OpenCode: `opencode.json` and agent frontmatter
|
|
239
|
+
- Antigravity: `.agents/rules/speckit-enterprise-safety.md`
|
|
240
|
+
|
|
241
|
+
## High-Risk Defaults To Block
|
|
242
|
+
|
|
243
|
+
Always deny or ask:
|
|
244
|
+
|
|
245
|
+
- `rm -rf`, `rmdir`, destructive cleanup
|
|
246
|
+
- `git reset --hard`, `git clean -fd`, force push
|
|
247
|
+
- `sudo`, `chmod -R`, `chown -R`
|
|
248
|
+
- shell access to `.env*`, `*.pem`, `*.key`, SSH keys, cloud credentials
|
|
249
|
+
- production deploys
|
|
250
|
+
- package publish
|
|
251
|
+
- arbitrary outbound network from shell
|
|
252
|
+
- external directory writes
|
|
253
|
+
|
|
254
|
+
## Recommendation
|
|
255
|
+
|
|
256
|
+
Implement permission rules as the next Speckit enterprise layer.
|
|
257
|
+
|
|
258
|
+
Priority:
|
|
259
|
+
|
|
260
|
+
1. Add central `.speckit/permissions.yaml`.
|
|
261
|
+
2. Extend `validateWorkflowContract` with permission checks.
|
|
262
|
+
3. Compile concrete adapter permission files.
|
|
263
|
+
4. Add tests that tamper with dangerous permissions and ensure validation fails.
|
|
264
|
+
|
|
265
|
+
This keeps Speckit aligned with the strongest common pattern across current agent IDEs: deny-first, least-privilege, phase-scoped, with explicit approval for risky operations.
|
package/docs/product-contract.md
CHANGED
|
@@ -5,13 +5,15 @@ Speckit owns the workflow contract for enterprise Agile + TDD development with a
|
|
|
5
5
|
## Speckit Owns
|
|
6
6
|
|
|
7
7
|
- The `.speckit/` source of truth: config, rules, workflows, templates, generated artifacts.
|
|
8
|
-
- The command surface: `init`, `doctor`, `start`, `memory`, `session`, `shape`, `plan`, `context`, `quick`, `sync`, `triage`, `sprint`, `graph`, `next`, `ready`, `run`, `review`, `close`.
|
|
8
|
+
- The command surface: `init`, `doctor`, `validate`, `start`, `memory`, `permissions`, `session`, `shape`, `plan`, `context`, `quick`, `sync`, `triage`, `sprint`, `graph`, `next`, `ready`, `run`, `review`, `close`.
|
|
9
9
|
- The skill catalog and super-agent routing contract for phase-specific agent behavior.
|
|
10
10
|
- IDE-specific initialization that installs the shared Speckit runtime plus only the selected IDE adapter when `--ide <name>` is provided.
|
|
11
11
|
- The long-session contract: project memory, active session state, checkpoints, compaction summaries, and resume handoffs.
|
|
12
|
+
- The workflow validation contract: phase order, core skills, super-agent routing, run prompt terms, and installed adapter prompt parity.
|
|
12
13
|
- The TDD gate: code stories require red, green, and refactor evidence.
|
|
13
14
|
- The adapter compiler for Claude Code, Codex, Antigravity, OpenCode, and Cursor.
|
|
14
15
|
- The safety policy for destructive commands, secrets, production access, and review readiness.
|
|
16
|
+
- The permission policy for privacy-sensitive files, context-heavy paths, destructive commands, and release commands.
|
|
15
17
|
|
|
16
18
|
## Speckit Delegates
|
|
17
19
|
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Project Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.1 - 2026-05-11
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- Prepared npm release for the workflow validation and permission policy package updates.
|
|
8
|
+
|
|
9
|
+
### Validation
|
|
10
|
+
|
|
11
|
+
- `npm test` passes with 36 tests.
|
|
12
|
+
|
|
3
13
|
## 0.3.0 - 2026-05-10
|
|
4
14
|
|
|
5
15
|
### Added
|
|
@@ -11,6 +21,11 @@
|
|
|
11
21
|
- Added `.speckit/agents/super-agent.md` as the portable orchestration router.
|
|
12
22
|
- Added `.speckit/skills/catalog.md` plus a curated Speckit skill set for shape, research, plan, context, graph, session, TDD, test, debug, review, docs, and ship phases.
|
|
13
23
|
- Added `.speckit/skills/schema.json` to define the portable skill contract and delegation statuses.
|
|
24
|
+
- Added `speckit validate` for workflow contract validation across phase order, skills, router, prompts, and installed adapters.
|
|
25
|
+
- Added `contractChecks` to `speckit doctor --deep --json`.
|
|
26
|
+
- Added `docs/use-cases.md` with command recipes and best practices for setup, migration, quick changes, full planning, long sessions, TDD, graph automation, review, CI, and troubleshooting.
|
|
27
|
+
- Added `.speckit/permissions.yaml` and `speckit permissions audit` for privacy, scout, destructive-command, and release-command checks.
|
|
28
|
+
- Added concrete permission output for Claude Code, Codex, and Cursor adapters.
|
|
14
29
|
- Added long-session prompt requirements for project memory, active session state, checkpoints, and compaction summaries.
|
|
15
30
|
- Added automatic `.beads/beads.jsonl` mirror generation so Beads Viewer robot commands can run without missing-project errors.
|
|
16
31
|
- Standard `speckit init --ide <name>` now installs the shared Speckit runtime, super-agent, and skill catalog for the selected IDE.
|
|
@@ -29,6 +44,7 @@
|
|
|
29
44
|
- Added workflow tests for memory/session state, sprint planning, and graph fallback.
|
|
30
45
|
- Expanded prompt quality tests for project memory, active session, checkpoint, and compaction.
|
|
31
46
|
- Added init and flow contract coverage for the curated skill catalog.
|
|
47
|
+
- Added workflow validator tests for passing and tampered contract states.
|
|
32
48
|
|
|
33
49
|
## 0.2.2 - 2026-05-10
|
|
34
50
|
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# Use Cases And Best Practices
|
|
2
|
+
|
|
3
|
+
This guide shows how to use Speckit for common product and engineering workflows.
|
|
4
|
+
|
|
5
|
+
## 1. New Project Setup
|
|
6
|
+
|
|
7
|
+
Use when a repository does not have Speckit runtime files yet.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx @trieungoctam/speckit@latest init --enterprise --ide cursor
|
|
11
|
+
npx @trieungoctam/speckit@latest doctor --deep
|
|
12
|
+
npx @trieungoctam/speckit@latest validate
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Use `--ide all` only when the team actively uses multiple IDEs in the same repository.
|
|
16
|
+
|
|
17
|
+
Best practices:
|
|
18
|
+
|
|
19
|
+
- Prefer one IDE adapter per project at first. Add more adapters after the team agrees on the workflow.
|
|
20
|
+
- Run `speckit validate` after init so broken prompts or missing skills are caught immediately.
|
|
21
|
+
- Commit generated Speckit files so every teammate gets the same flow.
|
|
22
|
+
- Keep `.speckit/` local to the repository. Avoid relying on global agent state for enterprise work.
|
|
23
|
+
|
|
24
|
+
## 2. Existing Project Migration
|
|
25
|
+
|
|
26
|
+
Use when a project already has code, tests, and team conventions.
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
speckit init --enterprise --ide all --force
|
|
30
|
+
speckit memory refresh
|
|
31
|
+
speckit doctor --deep --json
|
|
32
|
+
speckit validate --json
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Best practices:
|
|
36
|
+
|
|
37
|
+
- Read generated files before pushing. Keep project-specific rules in `.speckit/memory/project-context.md`.
|
|
38
|
+
- Do not start implementation during migration. First make the generated flow pass validation.
|
|
39
|
+
- If an adapter prompt conflicts with local policy, update the adapter generator, not only the generated file.
|
|
40
|
+
- Run project tests after migration even if Speckit only changed documentation and prompt files.
|
|
41
|
+
|
|
42
|
+
## 3. One Small Change
|
|
43
|
+
|
|
44
|
+
Use when the request is bounded and can fit into one story.
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
speckit memory refresh
|
|
48
|
+
speckit session start "Add invoice total validation"
|
|
49
|
+
speckit quick "Add invoice total validation"
|
|
50
|
+
speckit context .speckit/stories/<story>.md
|
|
51
|
+
speckit sync
|
|
52
|
+
speckit ready .speckit/stories/<story>.md
|
|
53
|
+
speckit run .speckit/stories/<story>.md
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Best practices:
|
|
57
|
+
|
|
58
|
+
- Use `quick` only for a single focused change.
|
|
59
|
+
- Do not skip `context`, `sync`, or `ready`. They prevent agents from implementing against stale assumptions.
|
|
60
|
+
- Keep acceptance criteria concrete enough to test.
|
|
61
|
+
- Checkpoint after the red test, green test, refactor, and review boundary.
|
|
62
|
+
|
|
63
|
+
## 4. Full Feature Planning
|
|
64
|
+
|
|
65
|
+
Use when the work includes multiple stories, architecture decisions, or team rollout.
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
speckit session start "Add subscription lifecycle"
|
|
69
|
+
speckit memory refresh
|
|
70
|
+
speckit shape "Add subscription lifecycle"
|
|
71
|
+
speckit plan "Add subscription lifecycle"
|
|
72
|
+
speckit sync
|
|
73
|
+
speckit sprint plan
|
|
74
|
+
speckit graph triage --json
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Best practices:
|
|
78
|
+
|
|
79
|
+
- Shape before planning. The shape artifact defines scope, non-goals, and risk.
|
|
80
|
+
- Keep stories small enough to pass through TDD evidence independently.
|
|
81
|
+
- Sync stories before graph commands so prioritization uses current metadata.
|
|
82
|
+
- Use `sprint next` or `graph triage` to select work instead of choosing only by recency.
|
|
83
|
+
|
|
84
|
+
## 5. Long Agent Session
|
|
85
|
+
|
|
86
|
+
Use when work will span many tool calls, handoffs, or context compaction.
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
speckit memory refresh
|
|
90
|
+
speckit session start "Refactor report export flow"
|
|
91
|
+
speckit session status --json
|
|
92
|
+
speckit session checkpoint --note "red complete"
|
|
93
|
+
speckit session checkpoint --note "green complete"
|
|
94
|
+
speckit session compact
|
|
95
|
+
speckit session resume <session-id>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Best practices:
|
|
99
|
+
|
|
100
|
+
- Durable files are the source of truth, not chat history.
|
|
101
|
+
- Write decisions to memory when they apply beyond the current story.
|
|
102
|
+
- Compact before handing work to another agent or after noisy command output.
|
|
103
|
+
- Use `DONE`, `DONE_WITH_CONCERNS`, `BLOCKED`, or `NEEDS_CONTEXT` in handoffs.
|
|
104
|
+
- Never pass a full conversation as handoff context. Pass files, acceptance criteria, constraints, and current status.
|
|
105
|
+
|
|
106
|
+
## 6. TDD Implementation
|
|
107
|
+
|
|
108
|
+
Use for all code stories.
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
speckit ready .speckit/stories/<story>.md
|
|
112
|
+
speckit run .speckit/stories/<story>.md
|
|
113
|
+
npm test
|
|
114
|
+
speckit session checkpoint --note "red evidence captured"
|
|
115
|
+
npm test
|
|
116
|
+
speckit session checkpoint --note "green evidence captured"
|
|
117
|
+
speckit session checkpoint --note "refactor validated"
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Best practices:
|
|
121
|
+
|
|
122
|
+
- Record test intent before code changes.
|
|
123
|
+
- Capture the red result before implementation.
|
|
124
|
+
- Make the smallest change that turns the failing test green.
|
|
125
|
+
- Refactor only while tests stay green.
|
|
126
|
+
- Put command output summaries in the evidence file. Avoid pasting large logs unless they are necessary.
|
|
127
|
+
|
|
128
|
+
## 7. Graph And Sprint Automation
|
|
129
|
+
|
|
130
|
+
Use when multiple stories exist and the next task is not obvious.
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
speckit sync
|
|
134
|
+
speckit sprint plan
|
|
135
|
+
speckit sprint next --json
|
|
136
|
+
speckit graph triage --json
|
|
137
|
+
speckit graph plan --json
|
|
138
|
+
speckit graph insights --json
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Best practices:
|
|
142
|
+
|
|
143
|
+
- Run `sync` before any graph command.
|
|
144
|
+
- Keep graph commands robot-safe. Use Speckit wrappers instead of interactive graph commands.
|
|
145
|
+
- Treat graph output as prioritization input, not automatic permission to implement.
|
|
146
|
+
- Re-run `sprint plan` when story status or dependencies change.
|
|
147
|
+
|
|
148
|
+
## 8. Review And Closure
|
|
149
|
+
|
|
150
|
+
Use after implementation and tests pass.
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
speckit review
|
|
154
|
+
speckit session compact
|
|
155
|
+
speckit close .speckit/stories/<story>.md
|
|
156
|
+
speckit sync
|
|
157
|
+
speckit validate
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Best practices:
|
|
161
|
+
|
|
162
|
+
- Review acceptance coverage before style or preference issues.
|
|
163
|
+
- Check TDD evidence, session freshness, docs impact, and graph sync.
|
|
164
|
+
- Close only after the story has current evidence and a clean handoff.
|
|
165
|
+
- Sync after closing so downstream graph and sprint views are current.
|
|
166
|
+
|
|
167
|
+
## 9. CI And Enterprise Rollout
|
|
168
|
+
|
|
169
|
+
Use when Speckit becomes a shared team standard.
|
|
170
|
+
|
|
171
|
+
Recommended CI checks:
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
npm run build
|
|
175
|
+
npm test
|
|
176
|
+
npx @trieungoctam/speckit@latest doctor --deep --json
|
|
177
|
+
npx @trieungoctam/speckit@latest validate --json
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Best practices:
|
|
181
|
+
|
|
182
|
+
- Fail CI when `validate` returns `needs-attention`.
|
|
183
|
+
- Require `doctor --deep` before release branches are merged.
|
|
184
|
+
- Keep generated prompt files reviewed like source code.
|
|
185
|
+
- Add new workflow phases only through the central contract and tests.
|
|
186
|
+
- Do not allow IDE-specific prompts to drift from the shared Speckit contract.
|
|
187
|
+
|
|
188
|
+
## 10. Troubleshooting
|
|
189
|
+
|
|
190
|
+
Use these checks when the workflow feels inconsistent.
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
speckit doctor --deep --json
|
|
194
|
+
speckit validate --json
|
|
195
|
+
speckit session status --json
|
|
196
|
+
speckit sync
|
|
197
|
+
speckit graph triage --json
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Common fixes:
|
|
201
|
+
|
|
202
|
+
- Missing skill file: run `speckit init --enterprise --ide <name> --force`.
|
|
203
|
+
- Adapter prompt mismatch: update the adapter generator, rebuild, then init again.
|
|
204
|
+
- Story is blocked: run `speckit context <story>` and `speckit sync`, then retry `speckit ready <story>`.
|
|
205
|
+
- Graph command fails: check that `.beads/beads.jsonl` exists after `speckit sync`.
|
|
206
|
+
- Session context is stale: run `speckit session checkpoint` and `speckit session compact`.
|