@gillcash/necktie 0.2.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.
Files changed (42) hide show
  1. package/.opencode/command/necktie-critique.md +5 -0
  2. package/.opencode/command/necktie-reverse.md +5 -0
  3. package/.opencode/command/necktie-review.md +5 -0
  4. package/.opencode/command/necktie.md +5 -0
  5. package/.opencode/plugins/necktie-frontmatter.cjs +12 -0
  6. package/.opencode/plugins/necktie.mjs +35 -0
  7. package/.qoder/rules/necktie.md +18 -0
  8. package/.qoder-plugin/plugin.json +13 -0
  9. package/AGENTS.md +18 -0
  10. package/LICENSE +22 -0
  11. package/NOTICE +7 -0
  12. package/README.es.md +84 -0
  13. package/README.ko.md +84 -0
  14. package/README.md +232 -0
  15. package/assets/logo-dark.png +0 -0
  16. package/assets/necktie-icon-pack-preview.png +0 -0
  17. package/commands/necktie-critique.toml +2 -0
  18. package/commands/necktie-reverse.toml +2 -0
  19. package/commands/necktie-review.toml +2 -0
  20. package/commands/necktie.toml +2 -0
  21. package/core/necktie-core.md +18 -0
  22. package/hooks/copilot-hooks.json +13 -0
  23. package/hooks/hooks.json +27 -0
  24. package/hooks/necktie-context.js +43 -0
  25. package/hooks/qoder-hooks.json +26 -0
  26. package/package.json +55 -0
  27. package/pi-extension/index.js +29 -0
  28. package/pi-extension/package.json +9 -0
  29. package/plugin.json +14 -0
  30. package/skills/necktie/SKILL.md +95 -0
  31. package/skills/necktie/agents/openai.yaml +6 -0
  32. package/skills/necktie/references/loop-protocol.md +85 -0
  33. package/skills/necktie/scripts/necktie_loop.py +199 -0
  34. package/skills/necktie-critique/SKILL.md +48 -0
  35. package/skills/necktie-critique/agents/openai.yaml +6 -0
  36. package/skills/necktie-reverse/SKILL.md +41 -0
  37. package/skills/necktie-reverse/agents/openai.yaml +6 -0
  38. package/skills/necktie-reverse/references/blueprint-template.md +54 -0
  39. package/skills/necktie-review/SKILL.md +61 -0
  40. package/skills/necktie-review/agents/openai.yaml +6 -0
  41. package/skills/necktie-review/references/reviewer-rubric.md +29 -0
  42. package/skills/necktie-review/scripts/validate_review.py +110 -0
@@ -0,0 +1,6 @@
1
+ interface:
2
+ display_name: "Necktie Reverse"
3
+ short_description: "Compile iterations into an executable brief"
4
+ default_prompt: "Use $necktie-reverse to compile this goal and critique into one executable brief."
5
+ policy:
6
+ allow_implicit_invocation: false
@@ -0,0 +1,54 @@
1
+ # Executable brief template
2
+
3
+ Use these headings. Omit a heading only when it is genuinely inapplicable.
4
+
5
+ ```text
6
+ ROLE
7
+ Act as [relevant role and perspective].
8
+
9
+ OUTCOME
10
+ Produce [artifact/action] so that [audience] can [real-world outcome].
11
+
12
+ INPUTS AND SOURCE HIERARCHY
13
+ - Evidence: [sources that may support claims]
14
+ - Method guidance: [sources that govern the process only]
15
+ - Constraints: [authority, safety, scope, format]
16
+ - Prior outputs: [hypotheses/style references only]
17
+ Resolve conflicts in this order: [explicit hierarchy].
18
+
19
+ SCOPE
20
+ Include: [...]
21
+ Exclude: [...]
22
+
23
+ MATERIAL ASSUMPTIONS
24
+ - [...]
25
+
26
+ WORKFLOW
27
+ 1. Inspect and classify sources.
28
+ 2. Establish a baseline and expose assumptions.
29
+ 3. Challenge the inquiry and identify the strongest unasked question.
30
+ 4. Produce the deliverable from raw evidence.
31
+ 5. Review against the acceptance criteria.
32
+ 6. Revise within the stated stopping rule.
33
+ 7. Verify in the target environment.
34
+
35
+ DELIVERABLES
36
+ - [exact output, format, and location]
37
+
38
+ ACCEPTANCE CRITERIA
39
+ - [observable pass/fail criterion]
40
+
41
+ EVIDENCE AND CITATION RULES
42
+ - [claim support, freshness, provenance, quotation, or uncertainty rules]
43
+
44
+ REVIEW CONTRACT
45
+ Return APPROVE, REVISE, or BLOCK. Report only material findings with exact evidence and the smallest required change. Ask the strongest unasked expert question. Do not rewrite while reviewing.
46
+
47
+ STOPPING RULE
48
+ Stop when all criteria pass, a material blocker requires user input, the same issue survives three reviews, or three revision decisions are recorded.
49
+
50
+ HANDOFF
51
+ Return the artifact, reusable brief, verification record, limitations, overlooked consideration, and strongest unasked question.
52
+ ```
53
+
54
+ Write instructions that are observable and testable. Replace adjectives such as "excellent" or "comprehensive" with explicit criteria. Do not request hidden chain-of-thought; request concise assumptions, decisions, evidence, and verification instead.
@@ -0,0 +1,61 @@
1
+ ---
2
+ name: necktie-review
3
+ description: Independently gate a candidate deliverable against its exact brief, evidence, acceptance criteria, and verification results. Use inside the Necktie loop, for an Auto-review-style approval decision, or when the user wants a concise APPROVE, REVISE, or BLOCK judgment with actionable material findings and a strongest unasked question.
4
+ ---
5
+
6
+ # Necktie Review
7
+
8
+ Judge the proposed result without silently rewriting it or expanding its scope. Review as a separate role from the author, using the smallest evidence packet that permits a reliable decision.
9
+
10
+ ## Inputs
11
+
12
+ Require:
13
+
14
+ - the exact executable brief;
15
+ - the candidate artifact or change set;
16
+ - acceptance criteria and constraints;
17
+ - a source ledger plus relevant evidence excerpts, paths, or links;
18
+ - verification results, if already available.
19
+
20
+ If an input is unavailable, decide whether that absence is itself a material finding. Read [references/reviewer-rubric.md](references/reviewer-rubric.md) completely before judging.
21
+
22
+ ## Review
23
+
24
+ 1. Inspect the candidate read-only. Do not author fixes while acting as reviewer.
25
+ 2. Test each acceptance criterion and every material factual claim against the evidence ledger.
26
+ 3. Look for omissions, contradictions, fabricated support, stale assumptions, unsafe actions, unusable formatting, and failure to verify in the target environment.
27
+ 4. Check whether the deliverable advances the actual goal rather than merely matching its requested shape.
28
+ 5. Identify what the author and user may not have considered.
29
+ 6. Name the single strongest unasked expert question and its consequence.
30
+ 7. Report only material findings. Combine duplicates and point to exact locations when possible.
31
+
32
+ ## Decide
33
+
34
+ - `APPROVE`: no critical or major finding remains. Minor optional improvements may be noted but cannot block approval.
35
+ - `REVISE`: one or more fixable critical or major findings remain. State the smallest required change for each.
36
+ - `BLOCK`: a required decision, authority, source, or safe execution path is absent and the author cannot resolve it within scope.
37
+
38
+ Confidence is not a substitute for evidence. Do not approve because the prose sounds plausible.
39
+
40
+ ## Output
41
+
42
+ Return only one JSON object matching this shape:
43
+
44
+ ```json
45
+ {
46
+ "decision": "APPROVE",
47
+ "summary": "One-sentence basis for the decision.",
48
+ "findings": [],
49
+ "strongest_unasked_question": "The highest-leverage omitted question.",
50
+ "question_consequence": "What changes if its answer differs.",
51
+ "confidence": "high"
52
+ }
53
+ ```
54
+
55
+ Each finding must contain `id`, `severity` (`critical`, `major`, or `minor`), `criterion`, `location`, `evidence`, and `required_change`. Use an empty findings array only for approval. Validate a saved decision with:
56
+
57
+ ```text
58
+ python skills/necktie-review/scripts/validate_review.py review.json
59
+ ```
60
+
61
+ Do not include markdown around the JSON, private reasoning, or an edited artifact.
@@ -0,0 +1,6 @@
1
+ interface:
2
+ display_name: "Necktie Review"
3
+ short_description: "Gate outputs against evidence and criteria"
4
+ default_prompt: "Use $necktie-review to independently review this deliverable."
5
+ policy:
6
+ allow_implicit_invocation: false
@@ -0,0 +1,29 @@
1
+ # Necktie reviewer rubric
2
+
3
+ ## Order of review
4
+
5
+ 1. **Authority and safety**: Is the action permitted, reversible where appropriate, and within the agreed scope?
6
+ 2. **Goal fit**: Does the artifact support the user's actual outcome and audience?
7
+ 3. **Evidence integrity**: Are material claims traceable to eligible, current sources? Are method context and prior outputs kept out of the evidence chain?
8
+ 4. **Correctness and completeness**: Are calculations, logic, content, and required sections accurate and sufficient?
9
+ 5. **Usability**: Can the intended audience use the result in its target environment?
10
+ 6. **Verification**: Were the relevant tests, renders, checks, or inspections performed, and do their results support completion?
11
+ 7. **Economy**: Is the solution no larger or more complex than the goal requires?
12
+
13
+ ## Severity
14
+
15
+ - `critical`: could cause unsafe action, material loss, invalid conclusion, unauthorized change, or failure of the central deliverable.
16
+ - `major`: violates an acceptance criterion, leaves a material unsupported claim, omits a necessary component, or prevents intended use.
17
+ - `minor`: real but non-blocking defect that does not change the decision or intended use.
18
+
19
+ Do not inflate preferences into findings. A valid finding states the violated criterion, exact location, concrete evidence, and smallest required change.
20
+
21
+ ## Decision examples
22
+
23
+ - Approve a sound artifact with an optional wording improvement; record it only as a minor finding if useful.
24
+ - Revise when a KPI definition lacks a denominator, a cited claim is unsupported, a requested file does not render, or a required test fails.
25
+ - Block when the user must choose between materially different scopes, required private data is unavailable, or execution needs authority the run does not possess.
26
+
27
+ ## Independence
28
+
29
+ Review the artifact that exists, not the artifact you imagine the author intended. Do not fix it during the review pass. If revising later, leave reviewer role and return to author role first.
@@ -0,0 +1,110 @@
1
+ #!/usr/bin/env python3
2
+ """Validate the JSON decision emitted by the Necktie reviewer skill."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import argparse
7
+ import json
8
+ from pathlib import Path
9
+ import sys
10
+
11
+
12
+ TOP_LEVEL_KEYS = {
13
+ "decision",
14
+ "summary",
15
+ "findings",
16
+ "strongest_unasked_question",
17
+ "question_consequence",
18
+ "confidence",
19
+ }
20
+ FINDING_KEYS = {"id", "severity", "criterion", "location", "evidence", "required_change"}
21
+ DECISIONS = {"APPROVE", "REVISE", "BLOCK"}
22
+ SEVERITIES = {"critical", "major", "minor"}
23
+ CONFIDENCE = {"high", "medium", "low"}
24
+
25
+
26
+ def nonempty_string(value: object) -> bool:
27
+ return isinstance(value, str) and bool(value.strip())
28
+
29
+
30
+ def validate_review(value: object) -> list[str]:
31
+ errors: list[str] = []
32
+ if not isinstance(value, dict):
33
+ return ["review must be a JSON object"]
34
+
35
+ missing = sorted(TOP_LEVEL_KEYS - value.keys())
36
+ extra = sorted(value.keys() - TOP_LEVEL_KEYS)
37
+ if missing:
38
+ errors.append(f"missing top-level keys: {', '.join(missing)}")
39
+ if extra:
40
+ errors.append(f"unexpected top-level keys: {', '.join(extra)}")
41
+ if missing:
42
+ return errors
43
+
44
+ decision = value["decision"]
45
+ if decision not in DECISIONS:
46
+ errors.append(f"decision must be one of: {', '.join(sorted(DECISIONS))}")
47
+ for field in ("summary", "strongest_unasked_question", "question_consequence"):
48
+ if not nonempty_string(value[field]):
49
+ errors.append(f"{field} must be a non-empty string")
50
+ if value["confidence"] not in CONFIDENCE:
51
+ errors.append(f"confidence must be one of: {', '.join(sorted(CONFIDENCE))}")
52
+
53
+ findings = value["findings"]
54
+ if not isinstance(findings, list):
55
+ errors.append("findings must be an array")
56
+ return errors
57
+
58
+ blocking_findings = 0
59
+ seen_ids: set[str] = set()
60
+ for index, finding in enumerate(findings):
61
+ prefix = f"findings[{index}]"
62
+ if not isinstance(finding, dict):
63
+ errors.append(f"{prefix} must be an object")
64
+ continue
65
+ missing_finding = sorted(FINDING_KEYS - finding.keys())
66
+ extra_finding = sorted(finding.keys() - FINDING_KEYS)
67
+ if missing_finding:
68
+ errors.append(f"{prefix} missing keys: {', '.join(missing_finding)}")
69
+ if extra_finding:
70
+ errors.append(f"{prefix} unexpected keys: {', '.join(extra_finding)}")
71
+ if missing_finding:
72
+ continue
73
+ for field in FINDING_KEYS - {"severity"}:
74
+ if not nonempty_string(finding[field]):
75
+ errors.append(f"{prefix}.{field} must be a non-empty string")
76
+ if finding["severity"] not in SEVERITIES:
77
+ errors.append(f"{prefix}.severity must be one of: {', '.join(sorted(SEVERITIES))}")
78
+ elif finding["severity"] in {"critical", "major"}:
79
+ blocking_findings += 1
80
+ if finding["id"] in seen_ids:
81
+ errors.append(f"duplicate finding id: {finding['id']}")
82
+ seen_ids.add(finding["id"])
83
+
84
+ if decision == "APPROVE" and blocking_findings:
85
+ errors.append("APPROVE cannot contain critical or major findings")
86
+ if decision in {"REVISE", "BLOCK"} and blocking_findings == 0:
87
+ errors.append(f"{decision} requires at least one critical or major finding")
88
+ return errors
89
+
90
+
91
+ def main(argv: list[str] | None = None) -> int:
92
+ parser = argparse.ArgumentParser(description=__doc__)
93
+ parser.add_argument("review", type=Path)
94
+ args = parser.parse_args(argv)
95
+ try:
96
+ value = json.loads(args.review.read_text(encoding="utf-8"))
97
+ except (OSError, json.JSONDecodeError) as exc:
98
+ print(f"invalid: {exc}", file=sys.stderr)
99
+ return 2
100
+ errors = validate_review(value)
101
+ if errors:
102
+ for error in errors:
103
+ print(f"invalid: {error}", file=sys.stderr)
104
+ return 1
105
+ print("valid Necktie review")
106
+ return 0
107
+
108
+
109
+ if __name__ == "__main__":
110
+ raise SystemExit(main())